[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 }