CF512D题解

首先这个遍历顺序和边是完全无关的。。。

于是可以看做一个排列。

对于每个点的遍历限制,假设访问某个点 \(u\) 的时候还连接着一条边 \(v\) 未被访问,那么 \(u\)\(v\) 连一条边。

在排列中就要求 \(v\)\(u\) 后面出现。

合法的太难考虑了,考虑不合法的。不合法的就是一定存在一个 \(u\) 会间接要求自己出现在自己的后面。

也就是定向后存在有向环。容易推导出环上的节点都不能在排列中出现。

缩点,将点双中的点全部删除,剩下一个森林。需要注意有一些树是不可能被遍历到的,点也需要被删掉。

如果我们对一棵树处理出了 \(F_T[k]\) 表示在 \(T\) 这棵树中遍历 \(k\) 个节点的方案数,那么剩下的部分就是对所有的 \(F_T\) 做二项卷积。

于是考虑如何处理 \(F_T[k]\) 数组。

考虑树形背包。特判掉 \(n=2\) 的情况。

\(f[u][k]\) 表示 \(u\) 的父亲节点未被访问,子树被访问了 \(k\) 个节点。显然只有当 \(k=siz[u]\) 时才能够继续向父亲节点转移。如果 \(k\ne siz[u]\) 则相当于对子树的背包做卷积,否则是 \(f[u][siz[u]]=siz[u]!\prod\frac{dp[v][siz[v]]}{siz[v]!}\)

\(g[u]\) 表示 \(u\) 的父亲节点及其“子树”全部被访问的方案数,那么枚举一个儿子钦定这个子树一定没有被访问完,具体计算求逆元即可。

最后做二项卷积,暴力做的复杂度是 \(O(n^2)\) 的。

使用拓扑排序即可寻找到符合条件的节点。

#include<cstdio>
const int M=105,mod=1e9+9;
int n,m,ege,h[M],f[M],inv[M],fac[M],ifac[M];int L,R,q[M],deg[M];
int siz[M],g[M],dp[M][M];int x,F[M],y,G[M],tmp[M];bool vis[M],typ[M];
struct Edge{
	int v,nx;
}e[M*M];
inline void Add(const int&u,const int&v){
	e[++ege]=(Edge){v,h[u]};h[u]=ege;++deg[u];
	e[++ege]=(Edge){u,h[v]};h[v]=ege;++deg[v];
}
inline int min(const int&a,const int&b){
	return a>b?b:a;
}
inline int pow(int a,int b=mod-2){
	int ans(1);for(;b;b>>=1,a=1ll*a*a%mod)if(b&1)ans=1ll*ans*a%mod;return ans;
}
inline void tpst(){
	for(int u=1;u<=n;++u)if(deg[u]<=1)vis[q[++R]=u]=true;L=1;
	while(L<=R){
		const int&u=q[L++];for(int v,E=h[u];E;E=e[E].nx)if(v=e[E].v,!vis[v]&&--deg[v]<=1)vis[q[++R]=v]=true;
	}
}
inline int Find1(const int&u,const int&fa){
	for(int v,E=h[u];E;E=e[E].nx)if((v=e[E].v)^fa){
		if(vis[v]){int V=Find1(v,u);if(~V)return V;}else return u;
	}
	return-1;
}
inline int Find2(const int&u,const int&fa){
	if(deg[u]>=2)return u;
	for(int v,E=h[u];E;E=e[E].nx)if((v=e[E].v)^fa&&vis[v]){
		int V=Find2(v,u);if(~V)return V;
	}
	return-1;
}
inline void close(const int&u){
	vis[u]=false;for(int v,E=h[u];E;E=e[E].nx)if(vis[v=e[E].v])close(v);
}
inline void DFS1(const int&u){
	int prod(1);dp[u][0]=1;
	for(int v,E=h[u];E;E=e[E].nx)if(v=e[E].v,v^f[u]&&vis[v]){
		static int tmp[M];f[v]=u;DFS1(v);prod=1ll*dp[v][siz[v]]*prod%mod;
		for(int i=0;i<=siz[u];++i)for(int j=0;j<=siz[v];++j){
			tmp[i+j]=(tmp[i+j]+1ll*dp[u][i]*dp[v][j])%mod;
		}
		siz[u]+=siz[v];for(int i=0;i<=siz[u];++i)dp[u][i]=tmp[i],tmp[i]=0;
	}
	++siz[u];dp[u][siz[u]]=1ll*prod*inv[siz[u]]%mod;
}
inline void DFS2(const int&u,const int&SZ,const int&rt){
	int prod(g[u]);for(int v,E=h[u];E;E=e[E].nx)if((v=e[E].v)^f[u])prod=1ll*dp[v][siz[v]]*prod%mod;
	for(int v,E=h[u];E;E=e[E].nx)if(v=e[E].v,v^f[u]&&vis[v]){
		const int&P=1ll*prod*pow(dp[v][siz[v]])%mod*inv[SZ-siz[v]]%mod;g[v]=P;DFS2(v,SZ,rt);
		for(int i=0;i<siz[v];++i)G[SZ-siz[v]+i]=(G[SZ-siz[v]+i]+1ll*P*dp[v][i])%mod;
	}
	if(u^rt)G[y]=(G[y]+1ll*prod*inv[y])%mod;
}
inline void Solve(const int&u){
	bool typ(false);int v=Find1(u,0);if(!~v)v=Find2(u,0),typ=true;
	if(!~v)return deg[u]?(G[0]=1,G[1]=2,G[2]=1,y=2):(G[0]=1,G[1]=1,y=1),close(u);
	DFS1(v);y=siz[v];for(int i=0;i<=y;++i)G[i]=dp[v][i];if(typ)g[v]=1,DFS2(v,y,v);close(v);
}
signed main(){
	scanf("%d%d",&n,&m);for(int u,v,i=1;i<=m;++i)scanf("%d%d",&u,&v),Add(u,v);
	fac[0]=ifac[0]=inv[0]=fac[1]=ifac[1]=inv[1]=1;for(int i=2;i<=n;++i)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	for(int i=2;i<=n;++i)fac[i]=1ll*fac[i-1]*i%mod,ifac[i]=1ll*ifac[i-1]*inv[i]%mod;tpst();F[x=0]=1;
	for(int u=1;u<=n&&(deg[u]=0,1);++u)for(int E=h[u];E;E=e[E].nx)++deg[u];
	for(int u=1;u<=n;++u)if(vis[u]){
		Solve(u);
		for(int i=0;i<=x;++i)for(int j=0;j<=y;++j)tmp[i+j]=(tmp[i+j]+1ll*F[i]*G[j])%mod;
		x+=y;y=0;for(int i=0;i<=x;++i)F[i]=tmp[i],tmp[i]=0;
	}
	for(int i=0;i<=n;++i)printf("%d\n",1ll*F[i]*fac[i]%mod);
}
posted @ 2022-07-05 09:35  Prean  阅读(19)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};