题意:n个数 q次询问,n<=3e4,q<=1e5 问[l,r]内不同的数之和?
用线段树离线处理,先把询问区间按照右端点排序.
每次在线段树中a[cur]最后一次出现的位置插入a[cur],直到某个区间右端点为止
xi为在[l,r]内最后一次出现的数,yi为[l,r]内被清0的数,yi被清0是因为后面有和yi相等的xi,所以不会少算 每个xi都不同也不会多算
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e4+7;
const int M=1e5+7;
ll c[N];
int n,m;
map<ll,ll> vis;
struct segment{
int l,r;
int mid()
{
return (l+r)>>1;
}
ll sum;
}a[N<<2];
struct node{
int l,r;
int id;
bool operator <(const node &t)const{
return r<t.r;
}
}q[M];
ll ans[M];
void push_up(int rt)
{
a[rt].sum=a[rt<<1].sum+a[rt<<1|1].sum;
}
void build(int l,int r,int rt)
{
a[rt].l=l,a[rt].r=r;
if(l==r)
{
a[rt].sum=0;//
return;
}
int m=a[rt].mid();
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
push_up(rt);
}
void update(int p,int v,int rt)
{
if(a[rt].l==a[rt].r&&a[rt].l==p)
{
a[rt].sum+=v;
return;
}
int m=a[rt].mid();
if(p<=m)
update(p,v,rt<<1);
else
update(p,v,rt<<1|1);
push_up(rt);
}
ll query(int l,int r,int rt)
{
ll res=0;
if(a[rt].l>=l&&a[rt].r<=r)
return a[rt].sum;
int m=a[rt].mid();
if(l<=m)
res+=query(l,r,rt<<1);
if(r>m)
res+=query(l,r,rt<<1|1);
return res;
}
int main()
{
int t;
cin>>t;
while(t--)
{
vis.clear();
cin>>n;
for(int i=1;i<=n;i++)
scanf("%I64d",&c[i]);
build(1,n,1);
cin>>m;
for(int i=0;i<m;i++)
{
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i;
}
sort(q,q+m);
int cur=1;
for(int i=0;i<m;i++)
{
for(;cur<=n&&cur<=q[i].r;cur++)
{
if(vis[c[cur]])
update(vis[c[cur]],-c[cur],1);
update(cur,c[cur],1);
vis[c[cur]]=cur;
}
ans[q[i].id]=query(q[i].l,q[i].r,1);
}
for(int i=0;i<m;i++)
printf("%I64d\n",ans[i]);
}
return 0;
}