21天好习惯第一期-11 【每日一题】Removeal

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值