HDU 5739 Fantasia

来源:互联网 发布:淘宝运营提成比例 编辑:程序博客网 时间:2024/06/05 19:24

题目链接: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);}
0 0