小记:树状数组局限思维在N数组上,然后一直想不出如何得解。 后来百度了一下,别人都是对M数组排序然后用树状数组。我看了一下思路后,自己动手,然后代码正确,改成long long 就A了。
思路:此题是求某一段区间内不重复的数的总和。起初我是对N用树状数组,然后一直想不出如何得到解。 然后看了别人的思路,发现自己被自己局限住了。唉~这就是差距啊。对M用树状数组,先要去对M数组以每段的终点值进行排序,然后从第一个N数开始,如果该数之前没出现过,就放到树状数组的该位置上。如果之前出现过,那么将之前该数在树状数组上的位置上删掉,然后在树状数组的当前位置上加上该数,即相当于,每个数在树状数组上都是保存在它最后出现的那个位置上。 这样做的原因是,我们是一段一段M区间进行处理的。那么我们处理完到某一段末尾时,就可以给出该段的结果了。sum(r) - sum(l-1)就是该段的结果。 这一点就是该做法的巧妙的地方。我们从1循环到n即可求出所有区间的answer。时间复杂度是O((N+M)logN)的样子,然后保存每一段的结果,最后输出即可。
每个数在之前是否出现过,用记录它在树状数组的位置的那个数组即可实现。只要不是初始值即代表出现过。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset(a,b,sizeof(a))
const int MAX_ = 1000010;
const int MAX = 50010;
struct node{
int l,r;
int id;
}a[MAX*4];
int loc[MAX_];
long long C[MAX], ans[MAX*4];
int p[MAX];
int lowbit(int x){return x&(-x);}
void add(int x,int num){
while(x < MAX){
C[x] += num;
x += lowbit(x);
}
}
long long sum(int x){
long long cnt = 0;
while(x > 0){
cnt += C[x];
x -= lowbit(x);
}
return cnt;
}
int cmp(const node& p, const node& q){
return p.r < q.r;
}
int main(){
int T, n, m;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
mst(C,0);
mst(loc,-1);
for(int i = 1; i <= n; ++i){
scanf("%d",&p[i]);
}
scanf("%d",&m);
for(int i = 0; i < m; ++i){
scanf("%d%d",&a[i].l,&a[i].r);
a[i].id = i;
}
sort(a,a+m,cmp);
int s = 1;
for(int i = 0; i < m; ++i){
while(s <= a[i].r){
if(loc[p[s]] == -1){
loc[p[s]] = s;
add(s,p[s]);
}
else {
int tmp = loc[p[s]];
add(tmp,-p[s]);
loc[p[s]] = s;
add(s,p[s]);
}
s++;
}
ans[a[i].id] = sum(a[i].r) - sum(a[i].l-1);
}
for(int i = 0; i < m; ++i){
printf("%I64d\n",ans[i]);
}
}
return 0;
}