CodeForces - 1000D:Yet Another Problem On a Subsequence (DP+组合数)
题目大意:这题目啊,贼难理解…
定义一个数列是“好的”:第一个数字a[0]为数列长度+1。
定义一个数列的子序列是“好的”:这个子序列能分割成几个(包括一个)“好的”数列。
各一个数列,求“好的”子序列的数目。
题目分析:想了好久没想出来。主要是方向想错了,我想的是dp[i]表示1~i有多少个,但是这样跑到第i个的时候很难往前去处理怎么结合。看了别人的题解顿悟了。
dp[i]表示从i-n,由data[i]打头的good subsequence个数。很显然我们枚举good sequence可以接的位置是 j = i+a[i]+1 到 n,转移方程就是dp[i] = c[ j -i -1][ a[i] ] * dp[j] 【乘法原理】。
dp[n + 1] = 1,想了想为什么要这样设,因为当2 1 1求dp[1]时下一个要接的位置是4,但是已经没有了,这是2 1 1本身就是一个good subsequence,dp[4]就是1。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e3 + 100;
const int mod = 998244353;
ll dp[maxn], data[maxn];
ll c[maxn][maxn];
int main()
{
int n;
scanf("%d", &n);
for(int i = 0; i <= n; i++) {
c[i][0] = c[i][i] = 1;
c[i][1] = i;
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
//这里处理组合数,而不是用逆元 学到了
}
}
for(int i = 1; i <= n; i++) {
scanf("%lld", &data[i]);
}
dp[n + 1] = 1;
for(int i = n; i > 0; i--) {
if(data[i] <= 0) continue;
for(int j = data[i] + i + 1; j <= n + 1; j++) {
dp[i] = dp[i] + (c[j - i - 1][data[i]] * dp[j]) % mod;
dp[i] %= mod;
}
}
ll sum = 0;
for(int i = 1; i <= n; i++)
sum = (sum + dp[i]) % mod;
printf("%lld\n", sum);
}