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; }