题目链接:戳这里
转自:戳这里
题意:
长度为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 ", dp[n][m]); 53 } 54 return 0; 55 }