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
",sum[w]);
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/wangfang20/p/3253908.html