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