CF2119D Token Removing 题解

题目传送门

思路

计数 DP 好题。

我们把问题转化为求出将 \(n\) 个标记分配到一些区间 \([l_i, r_i]\) 中,需要满足每一个 \(r_i\) 互不相同的方案数。其中如果要分配到区间 \([l_i, r_i]\),则 \(l_i = a_i\),需满足 \(a_i \neq 0\)。考虑 DP。设 \(dp_{i,j}\) 代表已经考虑了 \(i \sim n\) 这些标记(即已经确认了这些标记是否分配),且还有 \(j\) 个右端点没有用的方案数。为什么时考虑 \(i \sim n\)?因为如果第 \(i + 1\) 个标记可以用 \(j\) 这个右端点,那 \(i\) 也一定可以用。分两种情况讨论:

  1. 如果不将第 \(i\) 个标记放入任何区间,则此时 \(a_i = 0\),那 \(dp_{i,j} = dp_{i + 1, j - 1}\),代表现在多了 \(i\) 这个右端点没有用;

  2. 如果将第 \(i\) 个标记放入一个区间,则此时 \(l_i = a_i\) 可以填 \(1 \sim i\) 的任何一个数,而 \(r_i\) 又有 \(j + 1\) 个情况(即填完 \(i + 1 \sim n\) 时的 \(j\) 个右端点和 \(i\) 这个右端点,总共 \(j + 1\) 种选择右端点的方案)。所以 \(dp_{i,j} = i(j + 1) \cdot dp_{i + 1, j}\)

综上,\(dp_{i,j} = dp_{i + 1, j - 1} + i(j + 1) \cdot dp_{i + 1, j}\)。最终答案为 \(\sum \limits_{i = 0}^n dp_{1,i}\)。时间复杂度 \(\mathcal{O}(\sum n^2)\)

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 5005;

int t, n, mod;
long long dp[N][N];

void solve()
{
	scanf("%d%d", &n, &mod);
	dp[n + 1][0] = 1;
	for (int i = n; i; i--)
	{
		for (int j = 0; j <= n - i + 1; j++)
		{
			if (j > 0) dp[i][j] = dp[i + 1][j - 1];
			dp[i][j] = (dp[i][j] + 1ll * i * (j + 1) * dp[i + 1][j]) % mod;
		}
	}
	int ans = 0;
	for (int i = 0; i <= n; i++)
		ans = (ans + dp[1][i]) % mod;
	printf("%d\n", ans);
	for (int i = 1; i <= n + 1; i++)
		for (int j = 0; j <= n; j++)
			dp[i][j] = 0;
}

int main()
{
	scanf("%d", &t);
	while (t--) solve();
	return 0;
}
posted @ 2026-01-04 16:38  lucasincyber  阅读(0)  评论(0)    收藏  举报