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);
}