題面: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;
}
