#樹形dp#洛谷 3687 [ZJOI2017]仙人掌
阿新 • • 發佈:2021-10-19
樹形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; }