HDU 5739 Fantasia(点双连通分量+树形DP)

来源:互联网 发布:学士后java课程 编辑:程序博客网 时间:2024/04/28 22:00

Description
定义一个连通图的权值为所有顶点点权乘积,定义一个无向图的权值为这个无向图的极大连通子图权值和,现给出一张有n个点的无向图,每个点有点权wi,设删去节点i后此图权值为z[i],求这里写图片描述
Input
第一行一整数T表示用例组数,每组用例首先输入两个整数n和m表示点数和边数,之后输入n个整数wi表示每个点的点权,最后m行每行两个整数u个v表示u和v之间有一条边相连
(T<=1000,2<=n<=10^5,1<=m<=2*10^5,1<=wi<=10^9,sigma(n),sigma(m)<=1.5*10^6
Output
对于每组用例,输出一个整数S
Sample Input
1
3 2
1 2 3
1 2
2 3
Sample Output
20
Solution
先对原图用tarjan求一遍点双连通分量,对每个块用一个vector记录其中的点,对每个块新加一个点,重新建图,让这个点连向块内所有边,这样就可以得到一个至多block棵树的森林(block为块数),而且这个森林的连通性与原图连通性一致,从编号大的顶点为根节点对这张图dfs,令mul[i]表示以i节点为根的子树权值(如果i不是新加的点,那么这个子树必然是原图中一连通图),令sum[i]表示以i节点为根的所有子树权值和,为避免新加点对权值的影响,可以令新加的点的点权为1,在dfs过程中进行树形DP可以求出所有的mul和sum值,稍微分析下这棵树和dfs过程会发现以下几点:
1.孤立点的根节点是自身
2.非孤立点的根节点是新加节点
3.割点是非叶子节点
4.非割点都是叶子节点
令cnt为所有根节点的mul值之和
那么对于非孤立点i,删掉它后原图的权值就是mul[root[i]]/mul[i]+sum[i]+cnt-mul[root[i]]
对于孤立点i,删掉它原图的权值就是cnt-mul[i]
Code

#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>#include<vector>using namespace std;typedef long long ll;#define mod 1000000007ll#define maxn 111111vector<int>g[2*maxn];int low[maxn],dfn[maxn],stack[maxn];int index,top;int block;//块的数量 int bridge;//桥的数量 bool instack[maxn];vector<int>vec[maxn];//vec[i]表示第i个块中的点集,i从1~block void add_edge(int u,int v){    g[u].push_back(v),g[v].push_back(u);}void Tarjan(int u,int pre){    int v;    low[u]=dfn[u]=++index;    stack[top++]=u;    instack[u]=1;    for(int i=0;i<g[u].size();i++)    {        v=g[u][i];        if(v==pre)continue;        if(!dfn[v])        {            Tarjan(v,u);            if(low[u]>low[v])low[u]=low[v];            if(low[v]>dfn[u])bridge++;//找到一个桥             if(low[v]>=dfn[u])//找到一个割点u            {                block++;                vec[block].clear();                int vn;                do                {                    vn=stack[--top];                    vec[block].push_back(vn);                    instack[vn]=0;                }while(vn!=v);                vec[block].push_back(u);//割点可能还属于其他块所以不能出栈                  }        }        else if(instack[v]&&low[u]>dfn[v])            low[u]=dfn[v];    }}void solve(int n){    memset(dfn,0,sizeof(dfn));    memset(instack,0,sizeof(instack));    index=block=top=bridge=0;    for(int i=1;i<=n;i++)        if(!dfn[i])            Tarjan(i,-1);}int T,n,m,w[2*maxn],vis[2*maxn],root[2*maxn];ll mul[2*maxn],sum[2*maxn];void dfs(int u){    vis[u]=1,mul[u]=w[u],sum[u]=0;    for(int i=0;i<g[u].size();i++)    {        int v=g[u][i];        if(vis[v])continue;        root[v]=root[u];        dfs(v);        mul[u]=mul[u]*mul[v]%mod;        sum[u]=(sum[u]+mul[v])%mod;    }}ll mod_pow(ll a,ll b){    a%=mod;    ll ans=1;    while(b)    {        if(b&1)ans=ans*a%mod;        a=a*a%mod;        b>>=1;    }    return ans;}int main(){    scanf("%d",&T);    while(T--)    {        scanf("%d%d",&n,&m);        for(int i=1;i<=n;i++)scanf("%d",&w[i]),g[i].clear();        for(int i=0;i<m;i++)        {            int u,v;            scanf("%d%d",&u,&v);            add_edge(u,v);        }        solve(n);        for(int i=1;i<=n+block;i++)g[i].clear();        for(int k=1;k<=block;k++)        {            w[n+k]=1;            for(int i=0;i<vec[k].size();i++)            {                int v=vec[k][i];                add_edge(v,n+k);            }        }        memset(vis,0,sizeof(vis));        for(int i=n+block;i;i--)            if(!vis[i])                root[i]=i,dfs(i);        ll cnt=0,ans=0;        for(int i=1;i<=n+block;i++)            if(root[i]==i)cnt=(cnt+mul[i])%mod;        for(int i=1;i<=n;i++)        {            ll temp;            if(root[i]!=i)             {                temp=mul[root[i]]*mod_pow(mul[i],mod-2)%mod;                temp=((temp+sum[i])%mod+(cnt-mul[root[i]]+mod)%mod)%mod;            }            else temp=(cnt-mul[i]+mod)%mod;            ans=(ans+i*temp%mod)%mod;        }        printf("%I64d\n",ans);      }    return 0;}
0 0
原创粉丝点击