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\) 也一定可以用。分两种情况讨论:
-
如果不将第 \(i\) 个标记放入任何区间,则此时 \(a_i = 0\),那 \(dp_{i,j} = dp_{i + 1, j - 1}\),代表现在多了 \(i\) 这个右端点没有用;
-
如果将第 \(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;
}

浙公网安备 33010602011771号