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

题目

给定一个简单无向连通图,问有多少种加边方案使得这个图变成简单仙人掌。


分析

首先找到一棵生成树,考虑其它非树边所对应的树的路径上的边最多只能用一次,

这可以用树上差分做,如果一个点到其父节点的边被用了多次就一定无解。

否则没有被用过的部分将形成若干棵树,方案就是它们的乘积。

等于说问用若干条路径覆盖一棵树的方案。

设 \(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;
}
上一篇:HS6621串口透传模式 - [详解]


下一篇:深入解析go web框架macaron三-grafana的应用