题意:
一个由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]); } } }