题意:求一个序列中所有数字的和,其中数值相同的只能计算一次。
做法:可以先储存所有的请求,然后按照它们的右边界排序,在查询的同时更新区间。这里其实有一点点DP的味道,在它进行某个查询之前,保证所有的重复数字都被删除光了,并且有不能影响其他查询,所以呢,只能从最近的那个操作进行计算。1次query即可
#include<cstdio>
#include<cstring>
#include<algorithm>
#define left l,m,x<<1
#define right m+1,r,x<<1|1
#define LL __int64
const int LMT=50005;
using namespace std;
int a[LMT],pre[1000003];
LL ans[200003],sum[LMT<<2];
struct line
{
int l,r,id;
bool operator<(const line &y)const
{
return r<y.r;
}
}e[200003];
void init(void)
{
memset(pre,-1,sizeof(pre));
memset(sum,0,sizeof(sum));
}
void update(int op,int pos,int l,int r,int x)
{
if(l==r)
{
sum[x]=op;
return;
}
int m=(l+r)>>1;
if(pos<=m)update(op,pos,left);
if(pos>m)update(op,pos,right);
sum[x]=sum[x<<1]+sum[x<<1|1];
}
LL query(int L,int R,int l,int r,int x)
{
if(L<=l&&r<=R)
return sum[x];
LL res=0;
int m=(l+r)>>1;
if(L<=m)res+=query(L,R,left);
if(R>m)res+=query(L,R,right);
return res;
}
int main(void)
{
int T,n,m,i,st;
scanf("%d",&T);
while(T--)
{
init();
scanf("%d",&n);
for(i=0;i<n;i++)
scanf("%d",&a[i]);
scanf("%d",&m);
for(i=0;i<m;i++)
{
scanf("%d%d",&e[i].l,&e[i].r);
e[i].l--;e[i].r--;
e[i].id=i;
}
sort(e,e+m);
st=0;
for(i=0;i<n&&st<m;i++)
{
if(pre[a[i]]!=-1)update(0,pre[a[i]],0,n-1,1);
update(a[i],i,0,n-1,1);
while(st<m&&e[st].r<=i)
{
if(e[st].r==i)
ans[e[st].id]=query(e[st].l,e[st].r,0,n-1,1);
st++;
}
pre[a[i]]=i;
}
for(i=0;i<m;i++)printf("%I64d\n",ans[i]);
}
return 0;
}