每日一题:不同子序列个数(升级版)

题意

给定一个长度为 $n$ 的数组,求长度为 $n-m$ 的不同子序列个数。($1<=n<=1e5, m<=10$)

Solution

$dp[i][j]$ 表示长度为 $i$,删除 $j$ 个元素的子序列个数,不考虑重复的话,有 $dp[i][j] = dp[i-1][j] + dp[i-1][j-1]$(即已经删除了 $j$ 个和已经删除了 $j-1$ 个再删除这一个的情况)。

考虑去重。如果是单纯求不限长度的不同子序列的去重,容易得到:$dp[i] -= dp[pre[a[i]] - 1]$ ($pre[a[i]]$ 为上一次 $a[i]$ 出现的位置),在此题中也是同理,我们需要剔除 $[pre[a[i]], i]$ 之间的元素,假设我们当前需要剔除 $j$ 个元素,那么在$pre[a[i]]-1$之前我们先需要剔除 $j-(i-pre[a[i]])$ 个元素, $dp[i][j] -= dp[pre[a[i]]-1][j-(i-pre[a[i]])]$,初始化为所有的 $dp[i][0] = 1$。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
const int mod = 1e9 + 7;
int n, m, k, pre[N], dp[N][12];
int main() {
while (cin >> n >> m >> k) {
memset(dp, 0, sizeof(dp));
memset(pre, 0, sizeof(pre));
for (int i = 0; i <= n; i++) dp[i][0] = 1;
for (int i = 1; i <= n; i++) {
int x;
cin >> x;
for (int j = 1; j <= min(i, m); j++) {
dp[i][j] = (dp[i - 1][j] + dp[i - 1][j - 1]) % mod;
if (pre[x] && j - (i - pre[x]) >= 0)
dp[i][j] = (dp[i][j] - dp[pre[x] - 1][j - (i - pre[x])] + mod) % mod;
}
pre[x] = i;
}
cout << dp[n][m] << '\n';
}
return 0;
}
作者

Benboby

发布于

2020-04-24

更新于

2021-01-28

许可协议

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×