题解 CF512D 【Fox And Travelling】
这道题
首先,把输入的图求边双、缩点。如果一个边双有至少两个点,这个边双中的任何一个点就肯定不可能被遍历到(因为环里没有叶子)。所以,只需要考虑缩点之后的森林,其中有一些点不能选的情况下,遍历
森林里的每棵树显然是独立的。由于可以自由组合,答案的EGF就是每棵树答案的EGF的卷积。所以我们只需要会求每一棵树的答案,就可以用
考虑一棵树的答案是怎么求出来的。容易发现,最终被遍历到的点一定是这棵(无根)树的一些(两两不交的)子树。令
乍一看挺对的?然而这个等式其实会重复计算一些方案。比如说第一个样例,序列
但是这个等式在绝大多数情况下都是对的。准确地说,如果没有选中全部的点,就不会造成重复的计算。所以分成两部分考虑:选中了这棵树中所有的点时对答案的贡献;没有全部选中时对答案的贡献。整棵树的答案就是把这两部分贡献加起来就行了。
选中了一棵树中全部的点的方案数
注意这里用的是“方案数”而不是EGF,因为点数已经被固定了。
考虑枚举最后一个遍历到的点。假设其为
考虑dp。设
这里的
答案就是
存在至少一个点没有被选中时的EGF
随便选一个点
首先使用上一节的方法,求出以
这里的卷积暴力实现即可。复杂度分析和树上背包是一模一样的,每一对点只会在它们的
然后是换根的部分。我们通过一个DFS枚举每个点
从
考虑
这个暴力乘起来即可,复杂度是
代码如下:
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const ll mod=1e9+9;
int n,m,_,dfn[2010],low[2010],tp,id[2010],cnt;
int siz[2010],Rt,vis[2010],sz[2010],sta[2010],ban[2010];
ll fac[2010],ifc[2010],f[2010],g[2010][2010],h[2010],ans[2010],_ans[2010],F[2010],o[2010];
basic_string<int> vv[2010],v[2010];
ll po(ll a,ll b=mod-2){ll r=1;for(;b;b/=2,a=a*a%mod)if(b&1)r=r*a%mod;return r;}
void tarjan(int p,int fa){
dfn[p]=low[p]=++_;sta[++tp]=p;
for(int i:vv[p])if(!dfn[i])
tarjan(i,p),low[p]=min(low[p],low[i]);
else if(i!=fa)low[p]=min(low[p],dfn[i]);
if(low[p]==dfn[p]){
++cnt;int tot=0;
while(tp){
id[sta[tp]]=cnt;
tot++;
if(tot>1)ban[cnt]=1;
if(sta[tp--]==p)break;
}
}
}
void dfs(int p,int fa=0){
f[p]=siz[p]=1;vis[p]=1;
for(int i:v[p])if(i!=fa){
dfs(i,p);siz[p]+=siz[i];
(f[p]*=f[i]*ifc[siz[i]]%mod)%=mod;
}
g[p][0]=1;int sz=1;
for(int i:v[p])if(i!=fa){
memset(h,0,sizeof h);
for(int j=0;j<sz+siz[i];j++)
for(int k=0;k<=siz[i] && k<=j;k++)
(h[j]+=g[i][k]*g[p][j-k])%=mod;
sz+=siz[i];
for(int j=0;j<sz;j++)g[p][j]=h[j];
}
(f[p]*=fac[siz[p]-1])%=mod;
if(ban[p])f[p]=0;
g[p][siz[p]]=f[p]*ifc[siz[p]]%mod;
}
void dfs2(int p,int fa=0){
F[p]=sz[p]=1;
for(int i:v[p])if(i!=fa){
dfs2(i,p);sz[p]+=sz[i];
(F[p]*=F[i]*ifc[sz[i]]%mod)%=mod;
}
(F[p]*=fac[sz[p]-1])%=mod;
if(ban[p])F[p]=0;
}
void dfs1(int p,int fa=0){
ll op=o[p]*ifc[siz[Rt]-siz[p]]%mod;
for(int i=0;i<siz[p];i++)
(ans[i+siz[Rt]-siz[p]]+=g[p][i]*op)%=mod;
dfs2(p);
(ans[siz[Rt]]+=F[p]*ifc[siz[Rt]])%=mod;
for(int i:v[p])if(i!=fa){
o[i]=op;
for(int j:v[p])if(j!=fa && i!=j)(o[i]*=f[j]*ifc[siz[j]]%mod)%=mod;
(o[i]*=fac[siz[Rt]-siz[i]-1])%=mod;
if(ban[p])o[i]=0;
dfs1(i,p);
}
}
int main(){
scanf("%d%d",&n,&m);
ifc[0]=fac[0]=1;
for(int i=1;i<=n;i++)ifc[i]=po(fac[i]=fac[i-1]*i%mod);
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
vv[x]+=y,vv[y]+=x;
}
for(int i=1;i<=n;i++)if(!dfn[i])tp=0,tarjan(i,0);
for(int i=1;i<=n;i++)for(int j:vv[i])if(id[j]!=id[i] && !~v[id[j]].find(id[i]))
v[id[j]]+=id[i],v[id[i]]+=id[j];
_=1;_ans[0]=1;
for(int i=1;i<=cnt;i++)if(!vis[i]){
memset(ans,0,sizeof ans);
dfs(i);Rt=i;o[Rt]=1;
dfs1(Rt);
memset(h,0,sizeof h);
for(int i=0;i<_+siz[Rt];i++)
for(int j=0;j<=siz[Rt] && j<=i;j++)
(h[i]+=ans[j]*_ans[i-j])%=mod;
_+=siz[Rt];
memcpy(_ans,h,sizeof _ans);
}
for(int i=0;i<=n;i++)
printf("%lld\n",(_ans[i]*fac[i]%mod+mod)%mod);
return 0;
}