SoundHound Programming Contest 2018 Masters Tournament 本戦 C - Not Too Close

随便写写。

考虑一个分层图 \(\text{dp}\),记 \(f(i,j,k)\) 表示前 \(i\) 层,一共选了 \(j\) 个点,第 \(i\) 层选了 \(k\) 个点的方案数。

转移分两种情况:

  1. 层数 $ <= d$

那么枚举下一层选的点的个数 \(l\)

那么层内连边方案为 \(2^{\frac{l(l-1)}{2}}\),两层之间连边是 \((2^k-1)^l\)

同时选取一个点作为下一层的点的方案是 \(\displaystyle \binom{n - j - [i + 1 <= d]}{l - [i + 1=d]}\)

  1. 层数 $ > 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;
}
posted @ 2022-05-01 23:44  Nylch  阅读(26)  评论(1编辑  收藏  举报