链接:http://acm.hdu.edu.cn/showproblem.php?pid=3333
题意:给出n个数的一个序列,询问m次,每次询问区间[i,j]内改序列不同数之和,就是说该区间如果某个数出现多次,则只算一次。
这个题要从正面去做太难太难,反正我是不会。
线段树域保存区间不同数之和。
先把所有的询问保存下来,然后以右端点递增排序,遇到右端点相同的怎么排都无所谓。
然后从左到右跟新序列的值,更新到第i个值时检查之前有没有出现过与个这个值相等的,有的话就把之前那个节点赋0,再插入第i个数,如果查询区间右端点等于i,那么查询这个区间的答案并保存。
保存已更新数下标我用了个map,会比较慢,也可以离散来弄、、、
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<algorithm>
using namespace std;
#define ll __int64
#define MAXN 300050//30005会WA,不晓得为什么、、、
ll num[MAXN];
struct node
{
int l,r;
ll s;
}t[MAXN*4];
struct ele
{
int l,r;
ll ans;
int dix;
}a[MAXN];
bool cmp1(ele a,ele b)
{
return a.r<b.r;
}
bool cmp2(ele a,ele b)
{
return a.dix<b.dix;
}
void construct(int l,int r,int p)
{
t[p].l=l,t[p].r=r,t[p].s=0;
if(l==r) return ;
int m=(l+r)>>1;
construct(l,m,p<<1);
construct(m+1,r,p<<1|1);
}
void insert(int x,ll val,int p)
{
if(t[p].l==t[p].r)
{
t[p].s=val;
return ;
}
int m=(t[p].l+t[p].r)>>1;
if(x<=m) insert(x,val,p<<1);
else insert(x,val,p<<1|1);
t[p].s=t[p<<1].s+t[p<<1|1].s;
}
ll query(int l,int r,int p)
{
if(t[p].l==l&&t[p].r==r)
return t[p].s;
int m=(t[p].l+t[p].r)>>1;
if(r<=m) return query(l,r,p<<1);
else if(l>m) return query(l,r,p<<1|1);
else return query(l,m,p<<1)+query(m+1,r,p<<1|1);
}
int main()
{
int n,m,cas,i,j;
scanf("%d",&cas);
map<ll ,int> vis;
while(cas--)
{
vis.clear();
scanf("%d",&n);
construct(1,n,1);
for(i=1;i<=n;i++) scanf("%I64d",&num[i]),vis[num[i]]=0;
scanf("%d",&m);
for(i=1;i<=m;i++) scanf("%d%d",&a[i].l,&a[i].r),a[i].ans=0,a[i].dix=i;
sort(a+1,a+1+m,cmp1);
for(i=1,j=1;i<=n;i++)
{
if(vis[num[i]])
insert(vis[num[i]],0,1);
insert(i,num[i],1);
vis[num[i]]=i;
while(a[j].r==i)
{
a[j].ans=query(a[j].l,a[j].r,1);
j++;
if(j>m) break;
}
if(j>m) break;
}
sort(a+1,a+1+m,cmp2);
for(i=1;i<=m;i++)
printf("%I64d\n",a[i].ans);
}
return 0;
}