题面:https://www.luogu.com.cn/problem/CF512D
题意:给定一张\(n\)个点\(m\)条边的无向图。
一个点只有当与它直接相连的点中最多只有一个点未被遍历过时才可被遍历。
询问对于每个\(k\)\(\in\)[0,n],遍历\(k\)个点的方案数。
\(n\) \(\le\) 100,\(m\) \(\le\) \(\frac{n(n-1)}2\) ,答案对1e9+9取模。
题解:
先考虑哪些点可以被遍历到。显然,环上的点不可能被遍历到。
这样,我们把环去掉,就得到了一个森林。
但是这样之后,仍有点无法被遍历到。比如被夹在两个环之间的点。
用边双缩点判断显得太过繁琐,我们可以用类似拓扑排序的方法求出可以被遍历到的点。
之后,考虑每个连通块(树):
1.如果这棵树有节点与环相连,那么它一定是这棵树里最后被遍历到的点。
(可以思考一下为什么这样的点最多只有一个)
那么我们可以认为这个节点是树根,进行树上背包DP。
设\(f[i][j]\)表示在i的子树内有序地选了\(j\)个点的方案数。
转移时,枚举每个子树,然后把它们的\(f\)卷起来就好。
最后算\(i\)本身的贡献时,因为\(i\)是最后加进去的点(\(i\)的祖先一定在\(i\)后面被遍历到),
直接\(f[i][siz[i]]=f[i][siz[i]-1]\)即可。
2.如果没有节点与环相连,我们可以分别钦定树中的每个节点为根,然后做法和上述一样。
将所有情况的答案加起来,对于每个\(j\),还需要除以一个\(siz-j\),
因为当有siz-j个点未被遍历时,被遍历到的j个点的方案会在所有以这\(siz-j\)个点为根
做DP时被计算过。
最后把每棵树的贡献卷起来就大功告成了。
时间复杂度:\(O(\)n^3\()\)
代码:
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
typedef long long ll;
#define I inline void
#define IN inline int
#define C(x,y) memset(x,y,sizeof(x))
#define STS system("pause")
template<class D>I read(D &res){
res=0;register D g=1;register char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')g=-1;
ch=getchar();
}
while(isdigit(ch)){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
res*=g;
}
struct E{
int to,nt;
}e[40400];
#define T e[k].to
const int Mod=1e9+9;
int n,m,head[220],tot,X,Y,du[220],vis[220],v[220],root,C[220][220],ans[220],f[220][220],inv[220],now[220],son[220],siz[220];
I add(int x,int y){e[++tot].to=y;e[tot].nt=head[x];head[x]=tot;}
queue<int>q;
vector<int>vec;
IN Pow(int x,int y){
re res=1;
while(y){
if(y&1)res=(ll)res*x%Mod;
x=(ll)x*x%Mod;
y>>=1;
}
return res;
}
I findroot(int x,int fa){
v[x]=1;
vec.emplace_back(x);
for(re k=head[x];k!=-1;k=e[k].nt){
if(T==fa)continue;
if(!vis[T]){root=x;continue;}
findroot(T,x);
}
}
I Plus(int &x,int y){x+=y;if(x>=Mod)x-=Mod;}
I mul(int x,int y){
re t[220];
C(t,0);
F(i,0,n){
F(j,0,n){
Plus(t[i+j],(ll)((ll)f[x][i]*f[y][j]%Mod)*C[i+j][i]%Mod);
}
}
F(i,0,n)f[x][i]=t[i];
}
I D_1(int x,int fa){
C(f[x],0);siz[x]=1;f[x][0]=1;
for(re k=head[x];k!=-1;k=e[k].nt){
if(T==fa||!vis[T])continue;
D_1(T,x);siz[x]+=siz[T];
mul(x,T);
}
f[x][siz[x]]=f[x][siz[x]-1];
//cout<<x<<":";
//F(i,0,m)cout<<f[x][i]<<" ";
//cout<<endl;
}
int main(){
read(n);read(m);inv[0]=1;f[0][0]=1;C[0][0]=1;
F(i,1,n)inv[i]=Pow(i,Mod-2),C[i][0]=1;
F(i,1,n){
F(j,1,i)C[i][j]=(C[i-1][j]+C[i-1][j-1])%Mod;
}
C(head,-1);tot=-1;
F(i,1,m){
read(X);read(Y);
add(X,Y);add(Y,X);
du[X]++;du[Y]++;son[X]++;son[Y]++;
}
F(i,1,n)if(du[i]<=1)q.push(i),vis[i]=1;
while(!q.empty()){
m=q.front();q.pop();
for(re k=head[m];k!=-1;k=e[k].nt){
if(vis[T])continue;du[T]--;
if(du[T]<=1)q.push(T),vis[T]=1;
}
}
//F(i,1,n)cout<<vis[i]<<" ";
//cout<<endl;
F(i,1,n){
if(!vis[i]||v[i])continue;
vec.clear();
root=-1;findroot(i,0);
//cout<<"!";
//for(auto p:vec)cout<<p<<" ";
//cout<<endl;
if(root!=-1){
D_1(root,0);
//F(i,0,m)cout<<f[root][i]<<" ";
//cout<<endl;
mul(0,root);
//F(i,0,n)Plus(ans[i],f[root][i]);
}
else{
m=vec.size();C(now,0);
for(auto p:vec){
D_1(p,0);//F(i,0,m)cout<<f[p][i]<<" ";
//cout<<endl;
F(i,0,n)Plus(now[i],f[p][i]);
}
C(f[vec[0]],0);
F(i,0,m)f[vec[0]][i]=(ll)now[i]*inv[m-i]%Mod;
//F(i,0,m)cout<<f[vec[0]][i]<<" ";cout<<endl;
mul(0,vec[0]);
//F(i,0,n)Plus(ans[i],(ll)now[i]*inv[m-i]%Mod);
}
}
F(i,0,n)cout<<f[0][i]<<endl;
return 0;
}