CodeForces 1515E Phoenix and Computers

洛谷传送门

CF 传送门

思路

引理\(n\) 台电脑全部手动打开的方案数为 \(2^{n-1}\)
证明:设第一台打开的电脑是第 \(x\) 台。则 \(x+2\) 一定在 \(x+1\) 后打开,\(x+3\) 一定在 \(x+2\) 后打开,……,\(n\) 一定在 \(n-1\) 后打开。同理 \(x-2\) 一定在 \(x-1\) 后打开,\(x-3\) 一定在 \(x-2\) 后打开,……,\(1\) 一定在 \(2\) 后打开。\([x+1,n]\)\([1,x-1]\) 这两段相对顺序决定了,绝对顺序就穿插合并。想象有 \(n-1\) 个空,那么要选 \(x-1\) 个空供 \([1,x-1]\) 放,剩下 \(n-x\) 个空供 \([x+1,n]\) 放,方案数为 \(C_{n-1}^{x-1}\)。因此 \(n\) 台电脑全部手动打开的方案数为 \(\sum\limits_{x=1}^n C_{n-1}^{x-1} = 2^{n-1}\)

然后 dp。设 \(f_{i,j}\) 表示前 \(i\) 台电脑有 \(j\) 台是自动打开的方案数。有:

\[f_{i,0} = 2^{i-1} \]

\[f_{i,j} = \sum\limits_{k=2}^{i-1} f_{k-1,j-1} \times 2^{i-k-1} \times C_{i-j}^{i-k} \]

相当于枚举上一台自动打开的电脑 \(k\),则 \([k+1,i]\) 内的电脑都要手动打开,根据引理,此方案数为 \(2^{i-k-1}\)。总共需要手动打开的电脑有 \(i-j\) 台,\([k,i]\) 这一段内需要手动打开的电脑有 \(i-k\) 台,若这 \(i-k\) 台电脑的相对打开顺序已经确定了,将 \(i-k\) 台电脑插入 \(i-j\) 台电脑的方案数为 \(C_{i-j}^{i-k}\)

代码

code
/*

p_b_p_b txdy
AThousandMoon txdy
AThousandSuns txdy
hxy txdy

*/

#include <bits/stdc++.h>
#define pb push_back
#define fst first
#define scd second

using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;

const int maxn = 410;

ll n, mod, ifac[maxn], fac[maxn], f[maxn][maxn], pw[maxn];

ll qpow(ll b, ll p) {
	ll res = 1;
	while (p) {
		if (p & 1) {
			res = res * b % mod;
		}
		b = b * b % mod;
		p >>= 1;
	}
	return res;
}

ll C(ll n, ll m) {
	if (n < m || n < 0 || m < 0) {
		return 0;
	} else {
		return fac[n] * ifac[m] % mod * ifac[n - m] % mod;
	}
}

void solve() {
	scanf("%lld%lld", &n, &mod);
	fac[0] = 1;
	for (int i = 1; i <= n; ++i) {
		fac[i] = fac[i - 1] * i % mod;
	}
	ifac[n] = qpow(fac[n], mod - 2);
	for (int i = n - 1; ~i; --i) {
		ifac[i] = ifac[i + 1] * (i + 1) % mod;
	}
	pw[0] = 1;
	for (int i = 1; i <= n; ++i) {
		pw[i] = pw[i - 1] * 2 % mod;
	}
	for (int i = 1; i <= n; ++i) {
		f[i][0] = pw[i - 1];
		for (int j = 1; j <= (i - 1) / 2; ++j) {
			for (int k = 2; k < i; ++k) {
				f[i][j] = (f[i][j] + f[k - 1][j - 1] * pw[i - k - 1] % mod * C(i - j, i - k) % mod) % mod;
			}
		}
	}
	ll ans = 0;
	for (int i = 0; i <= n; ++i) {
		ans = (ans + f[n][i]) % mod;
	}
	printf("%lld\n", ans);
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

posted @ 2022-07-16 14:43  zltzlt  阅读(50)  评论(0)    收藏  举报