戳我传送


题意:


题目描述:
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;
}