Substrings(hdu 4455)

题意:

给定一个序列ai,个数为n。再给出一系列w;对于每个w,求序列中,所有长度为w的连续子串中的权值和,子串权值为子串中不同数的个数。

/*
    dp[i]表示长度为i的序列不同元素个数之和。
    考虑从i-1向i转移的时候,最后i-1个元素的贡献会消失,记last[i]表示最后i个元素中不重复元素的个数,则转移时,它的贡献是-last[i]。 
    同时每个序列会多出a[i],a[i+1],a[i+2]等元素,这些元素对答案有贡献的前提是与前i-1个元素不重复。
    那么i这个元素对答案有贡献的范围是pos[a[i]]-i+1~i+1(pos[i]表示i前面的元素是a[i]的位置),用树状数组累加一下。 
    所以转移方程为dp[i]=dp[i-1]+n-i+1-query(i)-last[i-1] 
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 1000010
#define lon long long
using namespace std;
int a[N],pos[N],c[N],last[N],n,m;
lon dp[N];
void updata(int x,int val){
    while(x<=n){
        c[x]+=val;
        x+=x&(-x);
    }
}
int query(int x){
    int tot=0;
    while(x){
        tot+=c[x];
        x-=x&(-x);
    }
    return tot;
}
int main(){
    while(1){
        scanf("%d",&n);
        if(!n) break;
        memset(pos,-1,sizeof(pos));
        memset(c,0,sizeof(c));
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int i=1;i<=n;i++){
            if(pos[a[i]]!=-1){
                updata(i-pos[a[i]]+1,1);
                updata(i+1,-1);
            }
            pos[a[i]]=i;
        }
        memset(pos,-1,sizeof(pos));
        memset(last,0,sizeof(last));
        for(int i=n;i;i--){
            last[n-i+1]=last[n-i];
            if(pos[a[i]]==-1) last[n-i+1]++;
            pos[a[i]]=i;
        }
        dp[1]=n;
        for(int i=2;i<=n;i++)
            dp[i]=dp[i-1]+(n-i+1-query(i))-last[i-1];
        scanf("%d",&m);
        for(int i=1;i<=m;i++){
            int x;scanf("%d",&x);
            printf("%I64d\n",dp[x]);
        }
    }
    return 0;
}

 

posted @ 2017-04-24 22:17  karles~  阅读(265)  评论(0编辑  收藏  举报