题意:
题目描述:
Bobo有整数s1,s2,...,sn,其中1 ≤ si ≤ k。
删除m个后问有多少种序列,然后对(1e9 + 7)取模。
输入描述:
输入包含多个测试用例,并以文件结尾终止。 每个测试用例的第一行包含三个整数n,m和k。 第二行包含n个整数s1,s2,...,sn。
输出描述:
对于每个测试用例,请打印一个表示结果的整数。
思路:
计数类dp。
表示前i个数字删掉j个元素的序列数。
如果不考虑重复,状态转移方程就是 , 表示取第i个数, 表示不取第i个数。
看数据就知道大部分案例一定会有很多重复的数字,接下来考虑重复的情况:
……1,4,7,8,5,1
1.这一段删除1,4,7,8,5 和删除4,7,8,5,1 会造成重复计算。
2.重复的方案数就是删除1,4,7,8,5 或删除4,7,8,5,1 后的序列数。
3.此时第一个1的位置是pos1,第二个1的位置是pos2,j=5,重复的部分就是f[pos1-1][0],意思是前面pos1-1个数一个都不减的序列数,也就是等于就是pos1前面序列数。
4.就是说,要想有重复的序列,一定是能删除完两个相同数字之间的数a[pos1]、a[pos2](包括前面的a[pos1]也要删除),也就是i-last[i]个,那么其实就是last[i]-1前面选一部分再加上这个重复的数字a[pos2]。
5.如果删除的数字 呢,取 ,在 前面删去k个数,再删去 不就又是重复的了吗。
6.如果 就不会有重复的。
7.所以如果有重复(即且 ),重复的部分是 。
Code:
#include <iostream> #include <algorithm> #include <queue> using namespace std; typedef long long ll; const int maxn=1e5+7; const int mod=1e9+7; template<class T>inline void read(T& res) { char c; T flag = 1; while ((c = getchar()) < '0' || c > '9') if (c == '-') flag = -1; res = c - '0'; while ((c = getchar()) >= '0' && c <= '9') res = res * 10 + c - '0'; res *= flag; } int a[maxn],pos[15],f[maxn][15]; int n, m, k; int main() { while(~scanf("%d%d%d",&n,&m,&k)) { for(int i=1;i<=n;++i) read(a[i]); fill(pos,pos+15,0); f[0][0]=1; for(int i=1;i<=n;++i) { for(int j=0;j<=m;++j) { if(!j) f[i][j]=f[i-1][j]; else f[i][j]=(f[i-1][j]+f[i-1][j-1])%mod; if(pos[a[i]]&&i-pos[a[i]]<=j) { f[i][j]=(f[i][j]-f[pos[a[i]]-1][j-(i-pos[a[i]])]+mod)%mod; } } pos[a[i]]=i; } printf("%d\n",f[n][m]); } return 0; }