[HDU5739]Fantasia(圆方树DP)

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

 

显然和点双有关。回忆各种tarjan:缩SCC得DAG,缩边BCC得一棵树,我们要想办法把点BCC也缩成一棵树。

tarjan求点双,然后给每个点双新建一个点,将这个BCC内的所有点连向这个点。

因为点与点之间没有边,SCC与SCC之间没有边,所以可以证明这是一棵树。这个算法有个名字叫Block Forest Data Structure.

缩成树之后随便选一个SCC点作为根,显然所有非叶子点都是割点(SCC虚拟点除外),叶子则都是非割点。

 

在这棵树上直接DP即可,为了方便计算直接将SCC点权赋为1。

 

接着是一些注意点:

  1. 孤立点要特殊处理,因为一个点不是点双。

  2. 缩点后的点的个数可能达到$2n$。

  3. 当前点为割点的判定:low[k]>=dfn[x]而不是low[x]==dfn[x],且这里要保证在此之前k未被访问过,具体看代码。

  4. tarjan弹栈的时候要注意,弹到k为止,x特殊处理。因为x和k在栈中可能不是连续的。

 

UPD:才知道这个就是圆方树。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define mem(a) memset(a,0,sizeof(a))
 5 #define rep(i,l,r) for (int i=(l),_=(r); i<=_; i++)
 6 #define For(i,x) for (int i=h[x],k; i; i=nxt[i])
 7 using namespace std;
 8 
 9 const int N=3000010,mod=1000000007;
10 int T,n,m,tot,top,u,v,S,tim,bcc,d[N];
11 int w[N],p[N],val[N],ans[N],stk[N],low[N],dfn[N],bel[N];
12 bool vis[N];
13 inline void up(int &x,int y){ x+=y; if (x>=mod) x-=mod; }
14 
15 int ksm(int a,int b){
16     int res=1;
17     for (; b; a=1ll*a*a%mod,b>>=1)
18         if (b&1) res=1ll*res*a%mod;
19     return res;
20 }
21 
22 struct E{
23     int cnt,h[N],to[N<<1],nxt[N<<1];
24     void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; }
25     
26     void dfs(int x,int fa){
27         vis[x]=1; val[x]=w[x]; bel[x]=tot;
28         For(i,x) if ((k=to[i])!=fa)
29             dfs(k,x),val[x]=1ll*val[x]*val[k]%mod;
30     }
31     
32     void DP(int x,int fa){
33         vis[x]=1; ans[x]=(S-p[bel[x]]+mod)%mod;
34         For(i,x) if ((k=to[i])!=fa) DP(k,x),up(ans[x],val[k]);
35         up(ans[x],1ll*p[bel[x]]*ksm(val[x],mod-2)%mod);
36     }
37 }G,G1;
38 
39 void tarjan(int x,int fa){
40     dfn[x]=low[x]=++tim; stk[++top]=x;
41     for (int i=G.h[x],k; i; i=G.nxt[i]) if ((k=G.to[i])!=fa){
42         if (dfn[k]) low[x]=min(low[x],dfn[k]);
43         else{
44             tarjan(k,x),low[x]=min(low[x],low[k]);
45             if (low[k]>=dfn[x]){
46                 bcc++; w[n+bcc]=1; int t;
47                 while (1){
48                     t=stk[top]; //printf("%d %d\n",t,n+bcc);
49                     G1.add(t,n+bcc); G1.add(n+bcc,t);
50                     top--; if (t==k) break;
51                 }
52                 G1.add(x,n+bcc); G1.add(n+bcc,x);
53             }
54         }
55     }
56 }
57 
58 void init(){ rep(i,0,n+bcc) G.h[i]=G1.h[i]=dfn[i]=d[i]=vis[i]=0; G.cnt=G1.cnt=tot=top=tim=bcc=S=0; }
59 
60 int main(){
61     freopen("hdu5739.in","r",stdin);
62     freopen("hdu5739.out","w",stdout);
63     for (scanf("%d",&T); T--; init()){
64         scanf("%d%d",&n,&m);
65         rep(i,1,n) scanf("%d",&w[i]);
66         rep(i,1,m) scanf("%d%d",&u,&v),G.add(u,v),G.add(v,u),d[u]++,d[v]++;
67         rep(i,1,n) if (!dfn[i]) tarjan(i,0);
68         rep(i,n+1,n+bcc) if (!vis[i]) tot++,G1.dfs(i,0),p[tot]=val[i],up(S,p[tot]);
69         rep(i,1,n) if (!d[i]) up(S,w[i]);
70         rep(i,1,n) if (!d[i]) ans[i]=(S-w[i]+mod)%mod;
71         //rep(i,1,n+bcc) printf("%d ",val[i]); puts("");
72         rep(i,0,n+bcc) vis[i]=0;
73         rep(i,n+1,n+bcc) if (!vis[i]) G1.DP(i,0);
74         //rep(i,1,n) printf("%d ",ans[i]); puts("");
75         int res=0;
76         rep(i,1,n) up(res,1ll*i*ans[i]%mod);
77         printf("%d\n",res);
78     }
79     return 0;
80 }

 

 

posted @ 2018-05-30 12:24  HocRiser  阅读(236)  评论(0编辑  收藏  举报