题目:
给一个序列a,询问一个区间[l,r],输出区间内不同数字的和。
(1 ≤ N ≤ 30000,1 ≤ Q ≤ 100000)
分析:
这种题看出来是线段树做,但是线段树不能只加一个区间里不同的数字。从线段树入手是不可能了,所以想办法怎么能让一个区间内相同的数字只被计算一次。既然树不可更改,说明求和的时候相同的数只有一个在树里,想办法实现这个就可以了。
核心就是从小区间往大区间一点一点扩展,这棵树肯定不是一下子建成的,肯定是一点一点往里加点,边加边查的。不然不可能使得任意一个区间内相同数字都只算一个。我们将询问的区间全保存下来,然后按照右边界排序,先算右边界最小的,往大的慢慢扩展。
假如当前该算[l1,r1]这个区间了,那么我们只关注[1,r1],r1往右的部分我们还不考虑。[1,r1]内如何让每个数字只被计算1次呢?每个数字我们只记录最靠右的那个,左边的就当成0。然后直接普通的区间求和就可以了。假如下一个区间是[l2,r2],怎么更新这个树呢?我们可以发现需要添加进树的就是[r1+1,r2]这一段,所以先遍历这一段,看这一段的数字之前是否出现过,之前出现过就把前面的在树里变成0,这次的加进树。这样对于[1,r2]而言,每个数字依然只出现了一次,这时查[l2,r2]直接普通区间求和,肯定是对的。
以此类推。
代码:
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <cstdio>
using namespace std;
#define ms(a,b) memset(a,b,sizeof(a))
#define lson rt*2,l,(l+r)/2
#define rson rt*2+1,(l+r)/2+1,r
typedef unsigned long long ull;
typedef long long ll;
const int MAXN=3e4+5;
const int MAXM=1e5+5;
const double EPS=1e-8;
const int INF=0x3f3f3f3f;
const int MOD = 1e9+7;
int n,Q;
ll a[MAXN],ans[MAXM],tree[MAXN<<2];
map<ll,int> Hash;
struct Que{
int l,r,id;
bool operator < (const Que &a) const{
return r < a.r;
}
}ques[MAXM];
void pushup(int rt){
tree[rt] = tree[rt<<1] + tree[rt<<1|1];
}
ll query(int L,int R,int rt,int l, int r){
if(L<=l && R>=r){
return tree[rt];
}
ll ret = 0;
if(L <= (l+r)/2) ret += query(L,R,lson);
if(R > (l+r)/2) ret += query(L,R,rson);
return ret;
}
void update(int rt,int l,int r,int pos,ll x){
if(l==r){
tree[rt] = x;
return;
}
if(pos <= (l+r)/2) update(lson,pos,x);
else update(rson,pos,x);
pushup(rt);
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
ms(ans,0);
ms(tree,0);
Hash.clear();
for(int i=1;i<=n;i++) scanf("%I64d",&a[i]);
scanf("%d",&Q);
for(int i=1;i<=Q;i++){
scanf("%d%d",&ques[i].l, &ques[i].r);
ques[i].id = i;
}
sort(ques+1,ques+Q+1);
int pos = 1;
for(int i=1;i<=Q;i++){
while(pos <= ques[i].r){
if(Hash[a[pos]])
update(1,1,n,Hash[a[pos]],0);
Hash[a[pos]] = pos;
update(1,1,n,pos,a[pos]);
pos++;
}
ans[ques[i].id] = query(ques[i].l,ques[i].r,1,1,n);
}
for(int i=1;i<=Q;i++){
printf("%I64d\n",ans[i]);
}
}
return 0;
}