CF512D 题解

CF512D 题解

$\text{Description}$

给定一张 $n$ 点 $m$ 边的有标号简单无向图,每次可以删除一个点度不超过 $1$ 的节点,对于每个 $k\in[0,n]$ 求出删除 $k$ 个点的方案数,答案对 $10^9+9$ 取模。

$n\le 100,m\le \frac{n(n+1)}{2}$。

$\text{Solution}$

首先环上的点是肯定无法删除的……我们做一遍拓扑排序把环上的点都筛出来扔掉,所以接下来讨论的点都是不在环上的。

剩下的点会构成一个森林,森林里的树可以被分为三种:

  1. 这棵树上没有点和环直接相连,可以看作一棵无根树;
  2. 这棵树上只有一个点和环直接相连,可以看作一棵有根树;
  3. 这棵树上至少有两个点和环直接相连,此时这棵树上的点也是不能被删除的,可以筛出来扔掉。

对于有根树,直接树形 DP 一下,注意由于有标号,所以要乘上组合数。

对于无根树,考虑先计算出树上每个点为根时的答案,然后相加,我们发现若有一个删除 $k$ 个点的方案,对于剩下没被删除的 $siz-k$ 个节点,以他们为根时都会计算进这个方案,故需要除以 $siz-k$。

最后合并时像树形 DP 一样合并即可,注意仍需乘上组合数。

$\text{Code}$
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#include<bits/stdc++.h>
#define REG register
#define LL long long
#define MAXN 105
#define MAXM 10005
using namespace std;
inline void read(int& x){
static char c;
while(!isdigit(c=getchar()));x=c^48;
while(isdigit(c=getchar()))x=(x*10)+(c^48);
}
const int Mod=1000000009;

inline int Add(int a,int b){return (a+b)%Mod;}
inline int Del(int a,int b){return (a-b+Mod)%Mod;}

int n,m;
int Q[MAXN],Deg[MAXN],front;
int InRing[MAXN],InTree[MAXN];
int TrRt[MAXN],TrSiz[MAXN],TNum;
int Ans[MAXN],DP[MAXN][MAXN],Siz[MAXN];
int Fac[MAXN],InvFac[MAXN];
int TrDP[MAXN][MAXN];

struct Edge{
int v,nxt;
}ed[MAXM<<1];
int head[MAXN],cnt;
inline void adde(int u,int v){ed[++cnt]=(Edge){v,head[u]},head[u]=cnt;}
inline void add(int u,int v){adde(u,v),adde(v,u),++Deg[u],++Deg[v];}
#define FORE(i,Now) for(REG int i=head[Now];i;i=ed[i].nxt)

inline int Pow(int a,int b){
int ans=1;
while(b){
if(b&1) ans=1ll*ans*a%Mod;
a=1ll*a*a%Mod,b>>=1;
}
return ans;
}

inline void Init(){
Fac[0]=InvFac[0]=1;
for(REG int i=1;i<=n;++i) Fac[i]=1ll*Fac[i-1]*i%Mod;InvFac[n]=Pow(Fac[n],Mod-2);
for(REG int i=n-1;i;--i) InvFac[i]=1ll*InvFac[i+1]*(i+1)%Mod;
}

inline int C(int n,int m){return 1ll*Fac[n]*InvFac[m]%Mod*InvFac[n-m]%Mod;}

inline void TopSort(){
for(REG int i=1;i<=n;++i) InRing[i]=1;
for(REG int i=1;i<=n;++i)
if(Deg[i]<=1) Q[++front]=i;
while(front){
int Now=Q[front--];
InRing[Now]=0;
FORE(i,Now){
int v=ed[i].v;
if(--Deg[v]<=1&&InRing[v]) Q[++front]=v;
}
}
}

void GetTr(int Now,int fa){
if(TrRt[TNum]==-1) return;
InTree[Now]=TNum,++TrSiz[TNum];
FORE(i,Now){
int v=ed[i].v;
if(v==fa) continue;
if(InRing[v]){
if(TrRt[TNum]==-1) return;
if(!TrRt[TNum]) TrRt[TNum]=Now;
else TrRt[TNum]=-1;
continue;
}
GetTr(v,Now);
}
}

int Tmp[MAXN];

void Dp(int Now,int fa){
Siz[Now]=1,DP[Now][0]=1;
FORE(i,Now){
int v=ed[i].v;
if(v==fa||InTree[v]^InTree[Now]) continue;
Dp(v,Now);
if(i==head[Now])
for(REG int j=0;j<=Siz[v];++j) Tmp[j]=DP[v][j];
else
for(REG int j=0;j<Siz[Now];++j)
for(REG int k=0;k<=Siz[v];++k)
Tmp[j+k]=Add(Tmp[j+k],1ll*DP[Now][j]*DP[v][k]%Mod*1ll*C(j+k,j)%Mod);
Siz[Now]+=Siz[v];
for(REG int j=0;j<Siz[Now];++j) DP[Now][j]=Tmp[j];
memset(Tmp,0,sizeof Tmp);
}
DP[Now][Siz[Now]]=DP[Now][Siz[Now]-1];
}

inline void Work(){
read(n),read(m);
Init();
for(REG int i=1;i<=m;++i){
int u,v;
read(u),read(v);
add(u,v);
}

TopSort();

for(REG int i=1;i<=n;++i){
if(!(InRing[i]||InTree[i])) ++TNum,GetTr(i,i);
else continue;
}

for(REG int i=1;i<=TNum;++i){
if(TrRt[i]==-1) continue;
memset(DP,0,sizeof DP);
if(TrRt[i]){
Dp(TrRt[i],0);
for(REG int j=0;j<=Siz[TrRt[i]];++j) TrDP[i][j]=DP[TrRt[i]][j];
}
else{
for(REG int j=1;j<=n;++j)
if(InTree[j]==i){
memset(DP,0,sizeof DP);
Dp(j,0);
TrSiz[i]=Siz[j];
for(REG int k=0;k<=TrSiz[i];++k) TrDP[i][k]=Add(TrDP[i][k],DP[j][k]);
}
for(REG int j=0;j<=TrSiz[i];++j) TrDP[i][j]=1ll*TrDP[i][j]*Pow(TrSiz[i]-j==0?1:TrSiz[i]-j,Mod-2)%Mod;
}
}

int SSiz=0;
for(REG int i=1;i<=TNum;++i){
if(TrRt[i]==-1) continue;
memset(Tmp,0,sizeof Tmp);
if(i==1)
for(REG int j=0;j<=TrSiz[i];++j) Tmp[j]=TrDP[i][j];
else{
for(REG int j=0;j<=SSiz;++j)
for(REG int k=0;k<=TrSiz[i];++k)
Tmp[j+k]=Add(Tmp[j+k],1ll*Ans[j]*TrDP[i][k]%Mod*C(j+k,j)%Mod);
}
SSiz+=TrSiz[i];
for(REG int j=0;j<=SSiz;++j) Ans[j]=Tmp[j];
}
Ans[0]=1;
for(REG int i=0;i<=n;++i) printf("%d\n",Ans[i]);
getchar();
}

int main(){Work();}
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×