1. 程式人生 > 其它 >#樹形dp#洛谷 3687 [ZJOI2017]仙人掌

#樹形dp#洛谷 3687 [ZJOI2017]仙人掌

樹形dp

題目

給定一個簡單無向連通圖,問有多少種加邊方案使得這個圖變成簡單仙人掌。


分析

首先找到一棵生成樹,考慮其它非樹邊所對應的樹的路徑上的邊最多隻能用一次,

這可以用樹上差分做,如果一個點到其父節點的邊被用了多次就一定無解。

否則沒有被用過的部分將形成若干棵樹,方案就是它們的乘積。

等於說問用若干條路徑覆蓋一棵樹的方案。

\(dp[x]\) 表示以 \(x\) 為根的子樹的方案數。

只需要考慮 \(x\) 的鄰邊如何連線,子樹的答案直接相乘。

\(f[n]\) 表示 \(n\) 條邊任意匹配的方案數,則

\(f[n]=f[n-2]*(n-1)+f[n-1]\)

那麼 \(dp[x]*=f[deg[x]]\)


程式碼

#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int N=500011,mod=998244353; struct node{int y,next;}e[N<<1],E[N<<1];
int f[N],c[N],v[N],as[N],hs[N],et=1,Et=1,n,dp[N],ans,fat[N],g[N],flag,upd,Lca[N<<1];
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline void print(int ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
inline signed getf(int u){return f[u]==u?u:f[u]=getf(f[u]);}
inline void Clear(){
	for (rr int i=1;i<=n;++i) c[i]=as[i]=hs[i]=0;
}
inline void dfs1(int x,int fa){
	f[x]=x,v[x]=upd,fat[x]=fa;
	for (rr int i=as[x];i;i=e[i].next)
	    if (e[i].y!=fa) dfs1(e[i].y,x),f[e[i].y]=x;
    for (rr int i=hs[x];i;i=E[i].next)
	    if (v[E[i].y]==upd) Lca[i]=Lca[i^1]=getf(E[i].y);
}
inline void dfs2(int x,int fa){
	for (rr int i=as[x];i;i=e[i].next)
	    if (e[i].y!=fa) dfs2(e[i].y,x),c[x]+=c[e[i].y];
}
inline void dfs3(int x,int TOP){
	rr int sub=0; dp[x]=1;
	for (rr int i=as[x];i;i=e[i].next)
	if (e[i].y!=fat[x]){
		if (!c[e[i].y]) dfs3(e[i].y,TOP),++sub,dp[x]=1ll*dp[x]*dp[e[i].y]%mod;
		    else dfs3(e[i].y,e[i].y);
	}
	if (x==TOP) ans=1ll*ans*dp[x]%mod*g[sub]%mod;
	    else dp[x]=1ll*dp[x]*g[sub+1]%mod;
}
signed main(){
	g[0]=g[1]=1;
	for (rr int i=2;i<N;++i) g[i]=(g[i-2]*(i-1ll)+g[i-1])%mod;
	for (rr int T=iut();T;--T,putchar(10)){
		n=iut(),et=Et=ans=flag=1,++upd;
		for (rr int i=1;i<=n;++i) f[i]=i;
		for (rr int TOT=iut();TOT;--TOT){
			rr int x=iut(),y=iut();
			if (getf(x)!=getf(y)){
				rr int fa=getf(x),fb=getf(y);
				if (fa>fb) fa^=fb,fb^=fa,fa^=fb;
				f[fa]=fb;
				e[++et]=(node){y,as[x]},as[x]=et;
				e[++et]=(node){x,as[y]},as[y]=et;
			}else{
			    E[++Et]=(node){y,hs[x]},hs[x]=Et;
			    E[++Et]=(node){x,hs[y]},hs[y]=Et;
			}
		}
		dfs1(1,0);
		for (rr int i=2;i<=Et;i+=2)
		    ++c[E[i].y],++c[E[i^1].y],c[Lca[i]]-=2;
		dfs2(1,0);
		for (rr int i=1;i<=n&&flag;++i)
		    if (c[i]>1) putchar(48),flag=0;
		if (!flag) {Clear(); continue;}
		dfs3(1,1),print(ans),Clear();
	}
	return 0;
}