Codeforces 1439D - INOI Final Contests(DP)

Codeforces 题面传送门 & 洛谷题面传送门

首先考虑 \(n=m\) 的情况,根据经典接链成环的模型,假设 \(dp1_i\) 表示 \(i\) 个人 \(i\) 个空位的方案数,那么可以得到 \(dp1_i=(i+1)^{i-1}·2^i\),具体方法就是添加一个 \(i+1\) 号位成为一个环,如果不会可以看那题题解,这里不赘述。

但是这题 \(n\ne m\) 显然没有那么多的性质,因此我们考虑如何用相对递推的方法求解这个 \(dp1_i\),稍微想想可以得到:

\[dp1_i=\sum\limits_{j=1}^idp1_{j-1}dp1_{i-j}(i+1)\dbinom{i-1}{j-1} \]

稍微解释一下这个式子。我们枚举最后一个到达的人的位置,假设为 \(j\)。那么最后一个人恰有 \(i+1\) 种选择:\((L,1),(L,2),\cdots (L,j),(R,j),(R,j+1),\cdots,(R,i)\),共有 \(i+1\) 种选择,因此式子里有个系数 \(i+1\)。而左右两边又分别是一个独立的问题——显然左边的点的 \(a\) 不可能在右边,右边的点的 \(a\) 不可能在左边,因此方案数可以直接用两边的 \(dp1\) 乘起来,再乘上从 \(i-1\) 个人中选 \(j-1\) 个人放在左边的方案数 \(\dbinom{i-1}{j-1}\) 得到。

但是题目所求并不是方案数,因此我们再考虑 \(dp2_i\) 表示对于所有 \(i\) 个人 \(i\) 个空位的方案,计算它们移动的距离之和。仿照 \(dp1\) 的转移方法同样可以得到

\[dp2_i=\sum\limits_{j=1}^i(dp1_{j-1}dp1_{i-j}(\dbinom{j}{2}+\dbinom{i-j+1}{2})+dp2_{j-1}dp1_{i-j}(i+1)+dp1_{j-1}dp2_{i-j}(i+1)) \]

这样我们就解决了 \(n=m\) 的情况。接下来考虑 \(n\ne m\) 的情况。

我们设 \(dp3_{i,j},dp4_{i,j}\) 分别表示 \(n=i,m=j\) 的方案数、距离之和,显然 \(i=j\)\(j=0\) 时方案数易求,考虑如何转移。我们枚举最后一段连续的被占用的座位长度 \(l\),那么有

\[dp3_{i,j}=\sum\limits_{l=0}^{\min(i-1,j)}dp1_ldp3_{i-l-1,j-l} \]

\[dp4_{i,j}=\sum\limits_{l=0}^{\min(i-1,j)}dp1_ldp4_{i-l-1,j-l}+dp2_ldp3_{i-l-1,j-l} \]

直接转移即可。时间复杂度 \(n^3\)

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 500;
int n, m, mod, dp1[MAXN + 5], dp2[MAXN + 5], c[MAXN + 5][MAXN + 5], dp3[MAXN + 5][MAXN + 5], dp4[MAXN + 5][MAXN + 5];
int main() {
	scanf("%d%d%d", &n, &m, &mod);
	for (int i = 0; i <= n; i++) {
		c[i][0] = 1;
		for (int j = 1; j <= i; j++) c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
	}
	dp1[0] = 1;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= i; j++) {
			dp1[i] = (dp1[i] + 1ll * dp1[j - 1] * dp1[i - j] % mod * c[i - 1][j - 1] % mod * (i + 1)) % mod;
			dp2[i] = (dp2[i] + 1ll * (1ll * dp1[j - 1] * dp1[i - j] % mod * (c[j][2] + c[i - j + 1][2]) % mod + 1ll * dp2[j - 1] * dp1[i - j] % mod * (i + 1) % mod
			+ 1ll * dp1[j - 1] * dp2[i - j] % mod * (i + 1) % mod) % mod * c[i - 1][j - 1]) % mod;
		}
	}
	for (int i = 0; i <= n; i++) {
		dp3[i][0] = 1; dp3[i][i] = dp1[i]; dp4[i][i] = dp2[i];
		for (int j = 1; j < i; j++) for (int l = 0; l <= min(j, i - 1); l++) {
			dp3[i][j] = (dp3[i][j] + 1ll * c[j][l] * dp3[i - l - 1][j - l] % mod * dp1[l]) % mod;
			dp4[i][j] = (dp4[i][j] + 1ll * (1ll * dp3[i - l - 1][j - l] * dp2[l] + 1ll * dp4[i - l - 1][j - l] * dp1[l]) % mod * c[j][l]) % mod;
		}
	}
	printf("%d\n", dp4[n][m]);
}
posted @ 2022-07-14 10:33  tzc_wk  阅读(77)  评论(0)    收藏  举报