题意:
求满足【n个点编号1-n,从1出发到各个点的最短路长度不等于k】的图有多少种,所有边长都为1,没有重边和自环,不连通则距离为无穷大
思路
题目中“不等于k”可以换成“小于k或者无穷大”。为了将小于k和无穷大分开,将所有点分为两类,第一类与1连通,第二类与1不连通,即为无穷大。分类后,第二类点和第一类点之间没有边,第二类点相互之间的边怎样连接也没有限制,问题简化为求第一类点中满足条件的方案数。
令
f[i][j][k]
表示:包含1的i个点的连通块中、从1出发到所有点的最短路的距离中、有j个距离恰好为k的方案数。
确定新状态:
f[i+x][x][k+1]
x个点内部连边方案数为
2x(x−1)/2
;x个点中每个点向j个最短路距离为k的点连边,可以任意连但不能一条都不连,所以方法有
2j−1种
,所以x个点共有
(2j−1)x
种;
i+x
个点中需要确定这x个点,所以这里有
Cxi+x
种。
因此建立转移方程:
f[i][j][k]
*
2x(x−1)/2
*
(2j−1)x
*
Cxi+x
—>
f[i+x][x][k+1]
回到原问题,先将n个点分成两类,假定第一类有i个点,且其中包含1,确定这
i−1
个点方案数有
Ci−1n−1
,第二类点则有
n−i
个,内部互连方案数为
2(n−i)(n−i−1)/2
,第一类点是个连通块,答案显然可以由f数组得到,即
∑f[i][1..i][1..k−1]
,因此最后答案为
∑f[i][1..i][0..k−1]
*
Ci−1n−1
*
2(n−i)(n−i−1)/2
离线预处理复杂度为
O(n4)
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
const int N = 62;
typedef long long ll;
ll fac[N], facinv[N], f[N][N][N], ans;
int n, K, T;
ll powermod(ll a, ll n, ll mod) {
ll ans = 1;
while (n) {
if (n & 1) ans = ans * a % mod;
a = a * a % mod;
n >>= 1;
}
return ans;
}
ll C(ll n, ll m, ll mod) {
return fac[n] * facinv[n - m] % mod * facinv[m] % mod;
}
ll F(int n) {
ll ans = 0;
for (int k = 0; k < K; k++) {
for (int j = 1; j <= n; j++) {
ans = (ans + f[n][j][k]) % mod;
}
}
return ans;
}
void init() {
fac[0] = facinv[0] = 1;
for (int i = 1; i < N; i++) {
fac[i] = fac[i - 1] * i % mod;
facinv[i] = facinv[i - 1] * powermod(i, mod - 2, mod) % mod;
}
f[1][1][0] = 1;
for (int i = 1; i < N; i++) {
for (int j = 1; j <= i; j++) {
for (int k = 0; k < i; k++) {
for (int x = 1; i + x < N; x++) {
f[i + x][x][k + 1] = (f[i + x][x][k + 1] + f[i][j][k] * powermod(powermod(2, j, mod) - 1, x, mod) % mod *
powermod(2, x * (x - 1) / 2, mod) % mod * C(i + x - 1, x, mod) % mod) % mod;
}
}
}
}
}
int main() {
//freopen("in.txt", "r", stdin);
init();
cin >> T;
while (T--) {
cin >> n >> K;
ans = 0;
for (int i = 1; i <= n; i++) {
ans = (ans + powermod(2, (n - i) * (n - i - 1) / 2, mod) * F(i) % mod * C(n - 1, i - 1, mod) % mod) % mod;
}
cout << ans << endl;
}
return 0;
}