HDU 3333 Turing Tree【树状数组+hash+离线】
题意: 知道了一段序列的值,有m 个询问,输出询问区间内不同数字的和。
分析:可以用离线的方法记录所有的询问,并按每个询问右区间的值排序,然后逐个插入数值,如果某个数值之前出现过,就将之前那个位置上的那个
数删除,并在新的位置上插入数值,如果当前插入数值的序号等于某个询问的右区间,就统计该询问区间的不同数值的值,由于数值比较大,需
要进行离散化,离散化可以用 hash 或 二分,将每个值映射到一个较小的数值上,以便用数组标记。
#include<stdio.h> #include<string.h> #include<stdlib.h> #include<algorithm> using namespace std; #define maxn 30003 #define clr(x)memset(x,0,sizeof(x)) int n; int lowbit(int x) { return (x)&(-x); } __int64 a[maxn]; void add(int pos,__int64 x) { while(pos<=n) { a[pos]+=x; pos+=lowbit(pos); } } __int64 getsum(int pos) { if(pos==0) return 0; __int64 sum=0; while(pos>0) { sum+=a[pos]; pos-=lowbit(pos); } return sum; } struct node { __int64 key; int num; }Hash[maxn<<2]; int hash_val(__int64 x) { int k=x%maxn; while(Hash[k].num) { if(Hash[k].key==x) break; k=(k+1)%maxn; } if(Hash[k].num==0) { Hash[k].key=x; Hash[k].num++; } return k; } struct query { int l,r,id; }q[100005]; bool cmp(query a,query b) { return a.r<b.r; } __int64 v[maxn]; __int64 res[100005]; int num[maxn]; int main() { int i,t,m; scanf("%d",&t); while(t--) { scanf("%d",&n); for(i=1;i<=n;i++) scanf("%I64d",&v[i]); scanf("%d",&m); for(i=0;i<m;i++) { scanf("%d%d",&q[i].l,&q[i].r); q[i].id=i; } sort(q,q+m,cmp); clr(Hash); clr(a); clr(num); int j=0; for(i=1;i<=n;i++) { int xu=hash_val(v[i]); int nx=num[xu]; if(nx) add(nx,-v[i]); add(i,v[i]); num[xu]=i; for(;j<m;j++) { if(q[j].r==i) res[q[j].id]=getsum(q[j].r)-getsum(q[j].l-1); else break; } } for(i=0;i<m;i++) printf("%I64d\n",res[i]); } return 0; }