SoundHound Programming Contest 2018 Masters Tournament 本戦 C - Not Too Close
随便写写。
考虑一个分层图 \(\text{dp}\),记 \(f(i,j,k)\) 表示前 \(i\) 层,一共选了 \(j\) 个点,第 \(i\) 层选了 \(k\) 个点的方案数。
转移分两种情况:
- 层数 $ <= d$
那么枚举下一层选的点的个数 \(l\)。
那么层内连边方案为 \(2^{\frac{l(l-1)}{2}}\),两层之间连边是 \((2^k-1)^l\)。
同时选取一个点作为下一层的点的方案是 \(\displaystyle \binom{n - j - [i + 1 <= d]}{l - [i + 1=d]}\)。
- 层数 $ > d$
枚举总点数,以及剩余点数,层内连边方案同理,同时剩余的点可以向非终点的点连边,方案同理。
于是状态是 \(\mathcal{O}(n^3)\),转移是 \(\mathcal{O}(n)\),总复杂度为 \(\mathcal{O}(n^4)\)。
#include <bits/stdc++.h>
typedef long long ll;
const int N = 160;
int n, D, mod;
ll f[N][N][N], binom[N][N], pw[N * N / 2 + 10];
ll fastpow(ll a, int b) {
ll ret = 1;
for(; b; b >>= 1, a = 1ll * a * a % mod) if(b & 1)
ret = 1ll * ret * a % mod;
return ret;
}
ll plus(ll a, ll b) {
return a + b >= mod ? a + b - mod : a + b;
}
ll reduce(ll a, ll b) {
return a - b < 0 ? a - b + mod : a - b;
}
ll mul(ll a, ll b) {
return 1ll * a * b % mod;
}
int main() {
scanf("%d%d%d", &n, &D, &mod);
if(D == n - 1) {
ll ans = 1;
for(int i = 1; i <= n - 2; ++i) ans = mul(ans, i);
printf("%lld\n", ans);
return 0;
}
if(D == 1) {
ll ans = fastpow(2ll, n * (n - 1) / 2 - 1);
printf("%lld\n", ans);
return 0;
}
pw[0] = 1;
for(int i = 1; i <= n * n / 2; ++i) pw[i] = mul(2ll, pw[i - 1]);
f[0][1][1] = 1, binom[0][0] = 1;
for(int i = 1; i <= n; ++i) {
binom[i][0] = binom[i][i] = 1;
for(int j = 1; j < i; ++j) binom[i][j] = plus(binom[i - 1][j], binom[i - 1][j - 1]);
}
for(int i = 0; i < D; ++i) {
for(int j = i; j <= n; ++j) {
for(int k = 1; k <= j - i; ++k) if(f[i][j][k]) {
// ll t = pw[k], pt = 1;
ll pt = 1;
for(int l = 1; j + l <= n - std::max(0, D - i - 1); ++l) {
// ll t1 = pw[l * (l - 1) / 2], t2 = binom[n - j - (i + 1 <= D)][l - (i + 1 == D)],
// t3 = mul(pt, t - 1);
pt = mul(pt, pw[k] - 1);
f[i + 1][j + l][l] = plus(f[i + 1][j + l][l], mul(f[i][j][k], mul(pw[l * (l - 1) / 2], mul(binom[n - j - (i + 1 <= D)][l - (i + 1 == D)], pt))));
// pt = mul(pt, t - 1);
}
}
}
}
ll ans = 0;
for(int i = D; i <= n; ++i) for(int j = 1; j <= i - D; ++j) {
// ll t1 = pw[(n - i) * (n - i - 1) / 2], t2 = pw[j * (n - i)];
ans = plus(ans, mul(f[D][i][j], mul(pw[(n - i) * (n - i - 1) / 2], pw[j * (n - i)])));
}
printf("%lld\n", ans);
return 0;
}