题意:
一个由n个整数组成的数列,求区间[L,R]上的和,其中相同的元素只能算一次。
题解:
对于原数组,hash记录每个元素出现的位置。然后把所有元素只添加一次进树状数组。离线初始数组和要查询的所有区间。把所有的区间按照L从小到大排序。
对于第一个区间,假设L=1,那么直接就sum(R)就是第一个区间的答案了,对于以后的区间如果L还是为1的话,继续sum(R)就是了。一直把所有的L=1的区间全部求出来.
然后去求L=2的区间,但是要把L=1的情况减去,只需要add(1,-a[1])即可,并且要把下次出现a[1]的x位置(hash记录了),执行add(x,a[1])。相当于前面L=1没出现过,L=2是起始位置了。依次这样走下去求出所有的答案。
如果区间中L并不存在为1,2等等的情况,直接减去,找到下次出现a[i]的位置,添加到树状数组即可。然后去找下一个L存在的即可。
#include<bits/stdc++.h>using namespace std;const int maxn = 50011;
const int maxm = 200000+100;
const int maxv = 1000000+100;
long long c[maxn];
int a[maxn];
long long ans[maxm];
struct node
{int l,r;int index;bool operator < (const node& a)const{return l<a.l;}
}ns[maxm];struct hashmap
{int head[maxv],next[maxn];void init(){memset(head,-1,sizeof head);}void inser(int i,int v){next[i] = -1;if(head[v]==-1)head[v]=i;else{int j = head[v];while(next[j]!=-1){j = next[j];}next[j] = i;}}int fin(int i){return next[i];}
}hm;long long sum(int x)
{long long res=0;while(x>0){res+=c[x];x-=(x&-x);}return res;
}void add(int x,int v)
{while(x<maxn){c[x]+=v;x+=(x&-x);}
}int main()
{int t;cin>>t;while(t--){int n,m;cin>>n;hm.init();memset(c,0,sizeof c);for(int i=1;i<=n;i++){scanf("%d",a+i);if(hm.head[a[i]]==-1)add(i,a[i]);hm.inser(i,a[i]);}cin>>m;for(int i=1;i<=m;i++){scanf("%d%d",&ns[i].l,&ns[i].r);ns[i].index=i;}sort(ns+1,ns+1+m);int j=1;for(int i=1;i<=n;i++) {while(ns[j].l==i){ans[ns[j].index]=sum(ns[j].r);///找完所有的区间为L的区间j++;}if(j>m)break;///所有的区间找完了add(i,-a[i]);///原来的位置减去a[i]if(hm.fin(i)!=-1)///在新的位置加上a[i]{add(hm.fin(i),a[i]);}}for(int i=1;i<=m;i++){printf("%lld\n",ans[i]);}}}