hdu 4455 动态规划

思路:用sum[i]表示区间长度为i的不相同数的个数和,假使所有的数都不相同,那么sum[i]=sum[i-1]+n-i+1-later[i-1]; later[i-1]表示的是序列最后面的长度为i-1的序列不同数的个数。这个式子的意义是每个长度为i-1的序列扩展为长度为i的序列,其不同数的个数会加1,一共有n-i+1个长度为i-1的序列能扩展,因为最后面的一个长度为i-1的序列肯定是扩展不了的(后面没数了),故要将最后面的长度为i-1的序列减去,即减later[i-1]。

那么对存在相同数的情况就是,任何数x,如果距离其上次出现的位置小于等于i-1,那么在n-i+1的基础上就要减去1。但这个并不好求,可以转换下;我们知道整个序列中一共存在n个x与pre[x]的关系,那么只要找出x-pre[x]>i-1的个数就行了,因为每个x-pre[x]>i-1在从长度为i-1扩展到长度为i时,都能为总和贡献1.故我们每次都用n减去所有长度小于等于i-1的关系个数。那么sum[i]=sum[i-1]-later[i-1]+n-Sum(i-1)。此处大粗的Sum是指求x-pre[x]小于等于i-1的个数和。

#include<cstring>

#include<algorithm>

#include<cstring>

#include<cmath>

#include<iostream>

#include<cstdio>

#define Maxn 1200010

using namespace std;

int pre[Maxn],interv[Maxn],later[Maxn],num[Maxn];

__int64 sum[Maxn];

int main()

{

    int n,m,q,i,j,w;

    while(scanf("%d",&n)!=EOF,n)

    {

        memset(pre,0,sizeof(pre));

        memset(interv,0,sizeof(interv));

        memset(later,0,sizeof(later));

        memset(sum,0,sizeof(sum));

        memset(num,0,sizeof(num));

        for(i=1;i<=n;i++){

            scanf("%d",num+i);

            //if(pre[num[i]])

            interv[i-pre[num[i]]]++;

            pre[num[i]]=i;

        }

        memset(pre,0,sizeof(pre));

        for(i=n;i>=1;i--)

        {

            if(!pre[num[i]])

                later[n-i+1]=later[n-i]+1,pre[num[i]]=1;

            else

                later[n-i+1]=later[n-i];

        }

        sum[1]=n;

        __int64 S=n;

        for(i=2;i<=n;i++)

        {

            sum[i]=sum[i-1]-later[i-1];

            S-=interv[i-1];

            sum[i]+=S;

        }

        scanf("%d",&m);

        for(i=1;i<=m;i++){

            scanf("%d",&w);

            printf("%I64d\n",sum[w]);

        }

    }

    return 0;

}

 

你可能感兴趣的:(动态规划)