HDU 5739 Fantasia
2016-07-25 21:12
351 查看
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5739
题目大意:给你一个森林,森林中的每个点有固定的价值,森林中树的价值为树中所有点的乘积,森林的价值为森林中所有树价值的总和,从1到n每次删除一个点,设删完点后森林的价值*对应得点的序号为Gi,问Gi(i从1到n)的总和是多少
解题思路:
预处理:预处理出每个树的价值,将每一个无根树通过DFS变为有根树,在DFS的过程中求出每一个点及其下所有子节点的乘积存储到数组val中,求出每一个点的dfs序存储到pre中,求出每一个点的low值,low值就是此点能连接到的dfs序中标号最小的点,存储到low中(不懂low值或pre是什么的请见刘汝佳入门经典-训练指南上的312页或者百度双连通分量,推荐前者)
具体操作:对于某个树,当要删除树中点 i 时,判断其所有子节点low[j]是否大于等于pre[i],如果是则说明删除点 i 后其子节点j和其下的所有点会变成单独的连通块,即刚才预处理出的val[j],用一个值ans2统计所有的val[j] ,再用一个值ans1表示删除点 i 后剩余节点的价值,用树的价值乘以i价值 的逆元和所有val[j]的逆元 就可以了。多个树稍微变变就好了。
AC代码:
题目大意:给你一个森林,森林中的每个点有固定的价值,森林中树的价值为树中所有点的乘积,森林的价值为森林中所有树价值的总和,从1到n每次删除一个点,设删完点后森林的价值*对应得点的序号为Gi,问Gi(i从1到n)的总和是多少
解题思路:
预处理:预处理出每个树的价值,将每一个无根树通过DFS变为有根树,在DFS的过程中求出每一个点及其下所有子节点的乘积存储到数组val中,求出每一个点的dfs序存储到pre中,求出每一个点的low值,low值就是此点能连接到的dfs序中标号最小的点,存储到low中(不懂low值或pre是什么的请见刘汝佳入门经典-训练指南上的312页或者百度双连通分量,推荐前者)
具体操作:对于某个树,当要删除树中点 i 时,判断其所有子节点low[j]是否大于等于pre[i],如果是则说明删除点 i 后其子节点j和其下的所有点会变成单独的连通块,即刚才预处理出的val[j],用一个值ans2统计所有的val[j] ,再用一个值ans1表示删除点 i 后剩余节点的价值,用树的价值乘以i价值 的逆元和所有val[j]的逆元 就可以了。多个树稍微变变就好了。
AC代码:
#include <iostream> #include <cstring> #include <cstdio> #include <cmath> #include <vector> #include <algorithm> #define RI(N) scanf("%d",&(N)) #define RII(N,M) scanf("%d %d",&(N),&(M)) #define RIII(N,M,K) scanf("%d %d %d",&(N),&(M),&(K)) #define mem(a) memset((a),0,sizeof(a)) using namespace std; const int inf=1e9+7; const int inf1=-1*1e9; double EPS=1e-10; typedef long long LL; int pre[100005];//节点的dfs序 int n,m; vector<int>G[100005];//图 int weight[100005];//节点权值 int treeNumber=0; int tree[100005];//每个子节点对应的树的标号 LL val[100005];//每个节点及其下所有子节点的价值成绩 LL treeValue[100005];//每个树的价值 LL ans=0,ans1=0; vector<int> son[100005];//每个节点的儿子 int low[100005]; /*节点是否为根节点,根节点会乘所有点的逆元,因为乘与所有点的逆元后想要得到的是0,但是为1,所有特殊判断*/ bool root[100005]; int clock=0; LL dfs(int rt,int fa) { int lowu=pre[rt]=++clock;//dfs序 1开始 tree[rt]=treeNumber; LL an=weight[rt]; for(int i=0;i<G[rt].size();i++) { int x=G[rt][i]; if(!pre[x]) { int lowv=dfs(x,rt); lowu=min(lowu,lowv); son[rt].push_back(x);//存储子节点 an=(an*val[x])%inf; } else if(pre[x]<pre[rt]&&x!=fa) { lowu=min(lowu,pre[x]); } } val[rt]=an;//存储val值 return low[rt]=lowu; } LL inv(LL aaaa);//求逆元 LL pow_m(LL x,LL nnn); int main() { int T; RI(T); while(T--) { RII(n,m); for(int i=1;i<=n;i++) RI(weight[i]); clock=0; memset(pre,0,sizeof(pre)); memset(root,0,sizeof(root)); for(int i=0;i<m;i++) { int u,v; RII(u,v); G[u].push_back(v); G[v].push_back(u); } treeNumber=1; for(int i=1;i<=n;i++) { if(!pre[i]) { dfs(i,0); treeValue[treeNumber]=val[i]; root[i]=true;//i为跟 treeNumber++;//树的个数 } } LL sum=0; for(int i=0;i<treeNumber;i++) sum=sum+treeValue[i]; ans=0; for(int i=1;i<=n;i++) { LL ans2=0;//变成连通块的子节点价值和 ans1=treeValue[tree[i]];//除去ans2和i的价值和 ans1=ans1*inv(weight[i])%inf; LL ss=sum-treeValue[tree[i]];//其余树的价值 for(int j=0;j<son[i].size();j++) { int v=son[i][j]; if(low[v]>=pre[i]) { ans1=ans1*inv(val[v])%inf; ans2=ans2+val[v]; } } if(root[i]) ans1=ans1-1;//根节点要减1 ans=(ans+(((ans1+ans2+ss)%inf)*i)%inf)%inf; } printf("%I64d\n",ans); for(int i=0;i<=n;i++) { G[i].clear(); son[i].clear(); } } return 0; } LL pow_m(LL x,LL nnn) { LL res=1; while(nnn>0){ if(nnn&1) res=(res*x)%inf; x=((x%inf)*(x%inf))%inf; nnn>>=1; } return res; } LL inv(LL aaaa) { return pow_m(aaaa,inf-2); }
相关文章推荐
- 连续子数组的最大和
- C. Replace To Make Regular Bracket Sequence
- 【POJ】-3253-Fence Repair(优先队列)
- BZOJ1452 [JSOI2009]Count
- 串结构练习——字符串连接
- Android 远程监控摄像头 移动端+PC端 旧手札变废为宝
- STM32F0在IAR新建工程
- poj 1328 Radar Installation
- 6.开放-关闭原则(Open-Close Principle)
- 关于spring.net的面向切面编程 (Aspect Oriented Programming with Spring.NET)-通知(Advice)API
- any_of 源码剖析
- RMQ 问题 2016.7.26
- SDKD 2016 Summer Single Contest #13 .B
- 简单的jqurey用法之一个事件控制多个元素
- 录像和拍照
- 2015CCPC南阳场 D - Pick The Sticks
- next_permutation函数
- 1051. 复数乘法 (15)
- Network in Network 学习总结
- LintCode笔记(4)——二叉树的层次遍历II