思路:用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; }