Link
首先我们进行拓扑排序,最后留下的点就是不能到达的点。
那么把这些点去掉之后,剩下的就是一个森林,而树有两类:
(1.)无根树:可以任意选择一个叶节点开始,然后可以在任意一个节点结束。
(2.)有根树:根节点与某个不能到达的点相邻,那么如果要到达根节点就必须在最后到达。
对于有根树,设(f_{i,j})表示在(i)的子树中选(j)个的方案数,合并是卷积的形式,为了表示顺序要乘上组合数。
对于无根树,枚举强制哪个节点最后到达,然后就变成了有根树的情况。注意对于(n)个点选(m)的方案数((n
e m)),一种情况会被计算(n-m)次,除掉即可。
时间复杂度为(O(n^3))。
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
const int N=107,P=1000000009;
int n,inv[N],C[N][N],deg[N],vis[N],fa[N],size[N],f[N][N],s[N],ans[N];std::vector<int>e[N],g[N];std::queue<int>q;
int read(){int x;scanf("%d",&x);return x;}
void inc(int&a,int b){a+=b-P,a+=a>>31&P;}
int mul(int a,int b){return 1ll*a*b%P;}
void dp(int u,int fa)
{
size[u]=0,f[u][0]=1,memset(f[u]+1,0,n<<2);
for(int v:g[u])
{
if(v==fa) continue;
dp(v,u),size[u]+=size[v];
for(int i=size[u];~i;--i) for(int j=1;j<=size[v]&&j<=i;++j) inc(f[u][i],mul(C[i][j],mul(f[u][i-j],f[v][j])));
}
++size[u],f[u][size[u]]=f[u][size[u]-1];
}
void dfs(int u,int fa)
{
for(int v:g[u]) if(v^fa) dfs(v,u);
dp(u,0);
for(int i=0;i<=n;++i) inc(s[i],f[u][i]);
}
int main()
{
n=read(),ans[0]=inv[0]=inv[1]=1;
for(int i=2;i<=n;++i) inv[i]=mul(P-P/i,inv[P%i]);
for(int i=0;i<=n;++i) for(int j=C[i][0]=1;j<=i;++j) inc(C[i][j]=C[i-1][j],C[i-1][j-1]);
for(int m=read(),u,v;m;--m) ++deg[u=read()],++deg[v=read()],e[u].push_back(v),e[v].push_back(u);
for(int i=1;i<=n;++i) if(std::sort(e[i].begin(),e[i].end()),deg[i]<2) q.push(i);
for(int u;!q.empty();)
{
vis[u=q.front()]=1,q.pop();
for(int v:e[u])
if(!vis[v])
{
fa[u]=v,g[u].push_back(v),g[v].push_back(u);
if(--deg[v]==1) q.push(v);
break;
}
}
for(int i=1;i<=n;++i)
if(vis[i])
{
if(!fa[i])
{
memset(s,0,(n+1)<<2),dfs(i,0);
for(int j=0;j<=size[i];++j) s[j]=mul(inv[size[i]-j],s[j]);
for(int j=n;j;--j) for(int k=0;k<j;++k) inc(ans[j],mul(C[j][k],mul(s[j-k],ans[k])));
}
else if(!vis[fa[i]])
{
g[i].erase(find(g[i].begin(),g[i].end(),fa[i])),dp(i,0);
for(int j=n;j;--j) for(int k=0;k<j;++k) inc(ans[j],mul(C[j][k],mul(f[i][j-k],ans[k])));
}
}
for(int i=0;i<=n;++i) printf("%d
",ans[i]);
}