题意:

给出一个长度为n且元素不超过k的序列,问删除m个数之后不同的序列有几种,模。其中

思路:

有转移

考虑到序列里面可能有重复数字,比如

1,3,2,4,1,5,这时如果1,3,2,4,1,5和1,3,2,4,1,5这两种情况应该是同一种方案,所以当前后有两个数字相同,其中一个删除,中间的也全部删除,这是会有重复方案的。

所以对于一个前面有相同数字的i,假设前一个数字为pre[i],相当于要减去pre[i]-1时候删除的数字为j-(i-pre[i]),就相当于当前i没有删除,将pre[i]~i-1全部删除,这种方案是重复的,所以要减去。

dp的初始化在循环中进行会快很多,memset比较慢。

代码:

typedef long long ll;
int cur[15];
int pre[100010];//上一个相同数字出现在哪里
ll dp[100010][15];
int main()
{
    int n,m,k;
    while(scanf("%d%d%d",&n,&m,&k)!=EOF)
    {
        int x;
        memset(pre,-1,sizeof(pre));
        memset(cur,-1,sizeof(cur));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&x);
            if(cur[x]!=-1)pre[i]=cur[x];
            cur[x]=i;
        }
        if(m>n){printf("0\n");continue;}
        /*for(int i=1;i<=n;i++)
            printf("%d ",pre[i]);
        printf("\n");*/
        //memset(dp,0,sizeof(dp));
        dp[0][0]=1;
        dp[1][0]=1;dp[1][1]=1;
        for(int i=2;i<=n;i++)
            for(int j=0;j<=min(m,i);j++)
        {
            dp[i][j]=0;
            if(j-1>=0&&j-1<=min(m,i-1))dp[i][j]=(dp[i][j]+dp[i-1][j-1])%MOD;
            if(j<=min(m,i-1))dp[i][j]=(dp[i][j]+dp[i-1][j])%MOD;
            if(pre[i]!=-1&&j-(i-pre[i])>=0&&j-(i-pre[i])<=min(m,pre[i]-1))
                dp[i][j]=(dp[i][j]-dp[pre[i]-1][j-(i-pre[i])]+MOD)%MOD;
        }
        /*for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=min(m,i);j++)
                printf("%lld ",dp[i][j]);
            printf("\n");
        }*/
        printf("%lld\n",dp[n][m]);
    }
    return 0;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

徐州邀请赛K 思维+最短路 Previous
NCPC2015A 思维+树的直径 Next