小记:树状数组局限思维在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; }