Removal
题意:
题目描述:
Bobo有整数s1,s2,…,sn,其中1 ≤ si ≤ k。
删除m个后问有多少种序列,然后对(1e9 + 7)取模。
输入描述:
输入包含多个测试用例,并以文件结尾终止。
每个测试用例的第一行包含三个整数n,m和k。
第二行包含n个整数s1,s2,…,sn。
输出描述:
对于每个测试用例,请打印一个表示结果的整数。
计数类dp。
f[i][j]f[i][j] 表示前i个数字删掉j个元素的序列数。
如果不考虑重复,状态转移方程就是f[i][j]=f[i-1][j]+f[i-1][j-1]f[i][j]=f[i−1][j]+f[i−1][j−1] ,f[i-1][j]\rightarrow f[i][j]f[i−1][j]→f[i][j] 表示取第i个数,f[i-1][j-1]\rightarrow f[i][j]f[i−1][j−1]→f[i][j] 表示不取第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.如果删除的数字j>i-last[i]j>i−last[i] 呢,取k=j-(i-last[i])k=j−(i−last[i]) ,在“1,4,7,8,5,1”“1,4,7,8,5,1” 前面删去k个数,再删去[pos1,pos2-1][pos1,pos2−1] 不就又是重复的了吗。
6.如果j<i-last[i]j<i−last[i] 就不会有重复的。
7.所以如果有重复(即last[a[i]]存在last[a[i]]存在且 i-last[a[i]]<=ji−last[a[i]]<=j ),重复的部分是f[last[a[i]]-1][j-(i-last[a[i]]f[last[a[i]]−1][j−(i−last[a[i]] 。
#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;
}