您的位置:首页 > 其它

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代码:

#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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: