码迷,mamicode.com
首页 > 其他好文 > 详细

HDU5739 Fantasia(点双连通分量 + Block Forest Data Structure)

时间:2016-07-22 21:17:23      阅读:289      评论:0      收藏:0      [点我收藏+]

标签:

题目大概说给一张无向点带有权无向图。定义连通图的权值为图中各点权的乘积,图的权值为其包含的各连通图的权和。设$z_i$为删除i点后图的权值,求$S = (\sum\limits_{i=1}^{n}i\cdot z_i) \text{ mod } (10^9 + 7)$。

 

官方题解这么说的:

显然, 只要删掉关键点才会使图不联通. 对于其他点, 权值很容易计算.

首先求出所有的点双联通分量, 对于每一个点双联通分量$S$, 新建一个节点$s$, 向$S$中每个节点$v$连边. 这样一来, 新增的点和原来图中的点会构成一个森林(据说这个有个名字, block forest data structure). 很容易观察到, 叶子节点肯定都是非关键点, 内部节点要么是关键点, 要么是新增的节点.

对于这个森林$F$, 删掉一个关键点或者一个叶子$i$之后, 会得到一个新森林$F_i$??, 这个$F_i$??对应的连通块集合和$G_i$对应的连通块集合其实是一样的(不考虑那些新增的点). 显然$G_i$的权值和$F_i$??的权值也是一样的, $F_i$的权值我们很容易通过树形dp算出来, 那么$G_i$的权值也随之而出.

可以在网上搜到关于用那个BF在线性时间计算所有关节点的影响的论文。。里面有这么一张图:

技术分享

这样就好理解了。

设新加圆形结点的权为1,在那棵构造出来的树中用dp求出各个结点的两个信息:

  • pro[u]表示u为根的子树内各个结点权值的乘积
  • sum[u]表示Σpro[v](u为原本图中的结点,v为u的孩子结点)

最后通过枚举要删除的各个点,再加上乘法逆元搞搞,就能直接通过这两个信息很快地求出删除某结点后新的总权值。

另外。。有个地方空间开太小WA了好久。。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 using namespace std;
  5 #define MAXN 111111
  6 #define MAXM 222222
  7 
  8 struct Edge{
  9     int v,flag,next;
 10 }edge[MAXM<<1];
 11 int NE,head[MAXN];
 12 void addEdge(int u,int v){
 13     edge[NE].v=v; edge[NE].flag=0; edge[NE].next=head[u];
 14     head[u]=NE++;
 15 }
 16 
 17 struct TEdge{
 18     int v,next;
 19 }tEdge[MAXM<<4];
 20 int tNE,tHead[MAXN<<1];
 21 void addEdge(int u,int v,int nothing){
 22     tEdge[tNE].v=v; tEdge[tNE].next=tHead[u];
 23     tHead[u]=tNE++;
 24 }
 25 
 26 int dn,dfn[MAXN],low[MAXN];
 27 int stack[MAXM],top;
 28 int root[MAXN],rn;
 29 
 30 void tarjan(int u,int rt){
 31     dfn[u]=low[u]=++dn;
 32     for(int i=head[u]; i!=-1; i=edge[i].next){
 33         if(edge[i].flag) continue;
 34         edge[i].flag=edge[i^1].flag=1;
 35         stack[++top]=i;
 36 
 37         int v=edge[i].v;
 38 
 39         if(dfn[v]){
 40             low[u]=min(low[u],dfn[v]);
 41             continue;
 42         }
 43 
 44         tarjan(v,rt);
 45         low[u]=min(low[u],low[v]);
 46 
 47         if(low[v]>=dfn[u]){
 48             ++rn;
 49             int k;
 50             do{
 51                 k=stack[top--];
 52                 root[edge[k].v]=rt;
 53                 root[edge[k^1].v]=rt;
 54                 addEdge(rn,edge[k].v,0);
 55                 addEdge(edge[k].v,rn,0);
 56                 addEdge(rn,edge[k^1].v,0);
 57                 addEdge(edge[k^1].v,rn,0);
 58             }while(edge[k^1].v!=u);
 59         }
 60     }
 61 }
 62 
 63 int n,weight[MAXN];
 64 
 65 bool vis[MAXN<<1];
 66 long long sum[MAXN<<1],pro[MAXN<<1];
 67 void dfs(int u){
 68     vis[u]=1;
 69     sum[u]=0; pro[u]=(u<=n) ? weight[u] : 1;
 70     for(int i=tHead[u]; i!=-1; i=tEdge[i].next){
 71         int v=tEdge[i].v;
 72         if(vis[v]) continue;
 73         dfs(v);
 74         if(u<=n){
 75             sum[u]+=pro[v];
 76             sum[u]%=1000000007;
 77         }
 78         pro[u]*=pro[v];
 79         pro[u]%=1000000007;
 80     }
 81 }
 82 
 83 long long ine(long long x){
 84     long long res=1;
 85     int n=1000000007-2;
 86     while(n){
 87         if(n&1){
 88             res*=x; res%=1000000007;
 89         }
 90         x*=x; x%=1000000007;
 91         n>>=1;
 92     }
 93     return res;
 94 }
 95 
 96 int main(){
 97     int t,m;
 98     scanf("%d",&t);
 99     while(t--){
100         scanf("%d%d",&n,&m);
101         for(int i=1; i<=n; ++i){
102             scanf("%d",weight+i);
103         }
104 
105         NE=0;
106         memset(head,-1,sizeof(head));
107         int a,b;
108         while(m--){
109             scanf("%d%d",&a,&b);
110             addEdge(a,b);
111             addEdge(b,a);
112         }
113 
114         dn=0; memset(dfn,0,sizeof(dfn));
115         rn=n; memset(root,0,sizeof(root));
116         top=0;
117         tNE=0; memset(tHead,-1,sizeof(tHead));
118         for(int i=1; i<=n; ++i){
119             if(dfn[i]==0) tarjan(i,rn+1);
120         }
121 
122         long long tot=0;
123 
124         memset(vis,0,sizeof(vis));
125         for(int i=1; i<=n; ++i){
126             if(vis[i]) continue;
127             if(root[i]){
128                 dfs(root[i]);
129                 tot+=pro[root[i]];
130                 tot%=1000000007;
131             }else{
132                 tot+=weight[i];
133                 tot%=1000000007;
134             }
135         }
136 
137         long long ans=0;
138 
139         for(int i=1; i<=n; ++i){
140             if(root[i]){
141                 ans+=(tot-pro[root[i]]+pro[root[i]]*ine(pro[i])%1000000007+sum[i])%1000000007*i;
142                 ans%=1000000007;
143             }else{
144                 ans+=(tot-weight[i])*i;
145                 ans%=1000000007;
146             }
147         }
148 
149         if(ans<0) ans+=1000000007;
150         printf("%lld\n",ans);
151     }
152     return 0;
153 }

 

HDU5739 Fantasia(点双连通分量 + Block Forest Data Structure)

标签:

原文地址:http://www.cnblogs.com/WABoss/p/5696926.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!