题解 P6596 【How Many of Them】
pythoner713 · · 题解
蒟蒻初学计数DP,这篇题解的代码是照着 lyd 书上的思路写的。希望对初学者有所帮助,望大佬轻喷。
题意十分清晰,求满足三个条件的连通图数量。
乍一眼看难以下手。因为「连通图」「割边」这些概念都是沿用自图论,但问题在于统计图的数量,而这又是一个数论问题。
如果我们从图论角度出发,不难想出一个暴力做法:对于包含
于是只能考虑用数学方法计算出答案,这时就要请出计数DP了。
计数DP,顾名思义,就是统计数量的DP。听上去好像没什么,但它统计的一般是诸如「方案数」等更抽象、难以统计的玩意儿,所以思维难度蛮高。光说不练假把戏,我们来到题练练手:
此处节点有序。是不是跟这题有点像?从DP角度考虑,不妨设
根据上面的分析,每条边可有可无,因此对于
代码实现:
for(int i = 2; i <= n; i++){
h[i] = qpow(2, i * (i - 1) / 2);
for(int j = 1; j < i; j++){
h[i] -= h[j] * C[i - 1][j - 1] % P * qpow(2, (i - j) * (i - j - 1) / 2) % P;
h[i] = (h[i] % P + P) % P;
}
}
(感觉我解释得好烂啊)。这便是计数DP的一道例题了(
我们接下来会发现这个
回到本题,它只是在上题的基础上加了割边不超过
没有割边的图就是双联通分量,于是我们照葫芦画瓢,考虑节点
然后考虑外部节点的连接方案数,我们开一个新数组记录。由于外部节点不一定连通,我们得多开一维记录有几个连通块:令
回到
综上所述,枚举
这时,将外部节点方案数,乘上内部节点方案数
这不就是书上的公式么。那么对于
上
惊奇地发现,前者就是本文开篇讲的
因此
for(int j = 1; j < i; j++){
for(int k = 1; k < i; k++){
int tmp = 0;
for(int x = 1; x <= min(i - k, j); x++){
tmp = (tmp + g[i - k][x][j - x] * qpow(k, x) % P) % P;
}
f[i][j] = (f[i][j] + f[k][0] * C[i - 1][k - 1] % P * tmp % P) % P;
}
}
f[i][0] = h[i];
for(int j = 1; j < i; j++){
f[i][0] -= f[i][j];
f[i][0] = (f[i][0] % P + P) % P;
}
最后考虑如何求出
至此,这道题大功告成。
刚才有个朋友问
#include<bits/stdc++.h>
#define int long long
#define P 1000000007
using namespace std;
int ans, n, m, _2[4000], C[60][60], f[60][60], g[60][60][60], h[60];
int qpow(int A, int B){
int r = 1;
while(B){
if(B & 1) r = (r * A) % P;
A = (A * A) % P, B >>= 1;
}
return r;
}
signed main(){
cin >> n >> m;
_2[0] = 1;
for(int i = 1; i <= 2500; i++) _2[i] = (_2[i - 1] << 1) % P;
for(int i = 0; i <= 50; C[i++][0] = 1){
for(int j = 1; j <= i; j++){
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % P;
}
}
h[1] = 1;
for(int i = 2; i <= n; i++){
h[i] = _2[i * (i - 1) / 2];
for(int j = 1; j < i; j++){
h[i] -= h[j] * C[i - 1][j - 1] % P * _2[(i - j) * (i - j - 1) / 2] % P;
h[i] = (h[i] % P + P) % P;
}
}
g[0][0][0] = 1;
for(int i = 1; i <= n; i++){
for(int j = 1; j < i; j++){
for(int k = 1; k < i; k++){
int tmp = 0;
for(int x = 1; x <= min(i - k, j); x++){
tmp = (tmp + g[i - k][x][j - x] * qpow(k, x) % P) % P;
}
f[i][j] = (f[i][j] + f[k][0] * C[i - 1][k - 1] % P * tmp % P) % P;
}
}
f[i][0] = h[i];
for(int j = 1; j < i; j++){
f[i][0] -= f[i][j];
f[i][0] = (f[i][0] % P + P) % P;
}
for(int j = 1; j <= i; j++){
for(int k = 0; k < i; k++){
for(int p = 1; p <= i; p++){
for(int q = 0; q <= k; q++){
g[i][j][k] = (g[i][j][k] + (f[p][q] * C[i - 1][p - 1] % P * p % P * g[i - p][j - 1][k - q] % P)) % P;
}
}
}
}
}
for(int i = 0; i <= min(m, n); i++){
ans = (ans + f[n][i]) % P;
}
cout << ans;
return 0;
}