2018牛客多校第一场 E-Removal【dp】

题目链接:戳这里

转自:戳这里

题意:
长度为n的序列,删掉m个数字后有多少种不同的序列。
n<=10^5,m<=10。


题解:
dp[i][j]表示加入第i个数字后,总共删掉j个数字时,有多少种不同的序列。
假设不考虑有重复的情况,dp方程为:dp[i][j]=dp[i-1][j] (第i个数字不删)+dp[i-1][j-1] (第i个数字删)。
现在考虑重复的情况。
如果前面有与a[i]相同的数字a[k] (k小于i),并且i-k<=j,就会产生重复。
比如:cdeaae(用字符串举例比较方便)

假设现在是i=6,j=4。那么我们需要dp[6][4]-dp[2][1]。
那么为什么不是减掉dp[3][1]呢。
因为dp[3][1]=dp[2][1]+dp[2][0],也就是说dp[3][1]还包括了删掉a[3]的状态,而如果删掉a[3],那么加入a[6]的时候就不会有重复了。所以减掉dp[2][1],就是减掉了a[3]不删除的情况。

附ac代码:

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cmath>
 4 #include<string>
 5 #include<vector>
 6 #include<algorithm>
 7 #include<cstdio>
 8 #include<queue>
 9 using namespace std;
10 typedef long long ll;
11 const ll mod = 1e9 + 7;
12 const int maxn = 1e5 + 10;
13 const int maxx = 2 * 1e3;
14 int nu[maxn];
15 int pos[11];
16 int pre[maxn];
17 ll dp[maxn][11];
18 int main()
19 {
20     int n, m, k;
21     while(~scanf("%d %d %d", &n, &m, &k))
22     {
23         memset(pos, 0, sizeof(pos));
24         memset(pre, 0, sizeof(pre));
25         memset(dp, 0, sizeof(dp));
26         for(int i = 1; i <= n; ++i)
27         {
28             scanf("%d", &nu[i]);
29             pre[i] = pos[nu[i]];
30             pos[nu[i]] = i;
31         }
32         for(int i = 1; i <= n; ++i)
33         {
34             dp[i][0] = 1;
35         }
36         for(int i = 0; i <= m; ++i)
37         {
38             dp[i][i] = 1;
39         }
40         for(int i = 1; i <= n; ++i)
41         {
42             for(int j = 1; j <= min(n - 1, 10); ++j)
43             {
44                 dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j]) % mod;
45                 if(pre[i] && (i - pre[i]) <= j)
46                 {
47                     dp[i][j] = (dp[i][j] - dp[pre[i] - 1][j - (i - pre[i])] + mod) % mod;
48                     //j-(i-pre[i])是指在不删去重复元素pre[i]时,前面还要删除的个数
49                 }
50             }
51         }
52         printf("%lld\n", dp[n][m]);
53     }
54     return 0;
55 }
View Code

猜你喜欢

转载自www.cnblogs.com/zmin/p/9358205.html