题目传送门:Problem - 3333
样例输入:
2
3
1 1 4
2
1 2
2 3
5
1 1 2 1 3
3
1 5
2 4
3 5
样例输出:
1
5
6
3
6
题目大意:
给你一段数字,q次询问,每次询问输出区间内不同数值的和,也就是说,区间内相同数只算一次。
解题思路:
一开始做这题的时候,刚学数据结构,就会几个板子,这题算是有难度的题了,放集训题单里,就是拿来防AK的,不过咱有万能的小伙伴,刷刷写出来了,也给我讲了思路,他是按询问的左边界排序,而我用的询问的右边界排序,也不懂什么是离线操作,这个大概就算吧。按照右边界排序之后,每次计算区间内最后一次出现的数,也就是没出现过的数,用更新树状数组,存储坐标,到下一次出现的时候,将树状数组对应位置归零,也就是加-a[i],再在新的位置更新,同时更新坐标,接着就是计算利用树状数组计算答案。
AC代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdio>
#include <map>
#include <cstring>
using namespace std;
using LL=long long;
const int N=3e4+5;
const int M=1e5+5;
int n,q;
LL a[N];
LL tree[N];
int l[N],r[N];
map <LL,int> mp;
struct node{
int l,r;
int i;
}h[M];
LL ans[M];
bool cmp(struct node a,struct node b)
{
if(a.r==b.r) return a.l<b.l;
else return a.r<b.r;
}
int lowbit(int t)
{
return t&-t;
}
void update(int idx,LL val)
{
while(idx<=n)
{
tree[idx]+=val;
idx+=lowbit(idx);
}
}
LL query(int idx)
{
LL res=0;
while(idx)
{
res+=tree[idx];
idx-=lowbit(idx);
}
return res;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for (int i=1; i<=n; i++) scanf("%lld",&a[i]);
scanf("%d",&q);
for (int i=1;i<=q; i++) {
scanf("%d%d",&h[i].l,&h[i].r);
h[i].i=i;
}
sort(h+1, h+q+1, cmp);
memset(l, 0, sizeof(l));
memset(r, 0, sizeof(r));
memset(tree, 0, sizeof(tree));
int tem=1;
l[h[tem].r]=1;
r[h[tem].r]=1;
for (int i=2; i<=q; ) {
while(h[i].r==h[tem].r&&i<=q) i++;
l[h[tem].r]=tem;
r[h[tem].r]=i-1;
tem=i;
}
mp.clear();
for (int i=1; i<=n; i++) {
if(!mp[a[i]])
{
mp[a[i]]=i;
update(i, a[i]);
}else{
update(mp[a[i]], -a[i]);
update(i, a[i]);
mp[a[i]]=i;
}
if(!r[i]) continue;
int L=l[i],R=r[i];
LL x=query(i);
for (int i=L; i<=R; i++) {
LL ans1=x;
ans1-=query(h[i].l-1);
ans[h[i].i]=ans1;
}
}
for (int i=1; i<=q; i++) printf("%lld\n",ans[i]);
}
return 0;
}