#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define lowbit(x) ((x)&(-x)) #define maxn 30003 #define maxm 100001 __int64 c[maxn],f[maxm]; int m,n,a[maxn],b[maxn],pre[maxn]; struct node{ int l,r,id; }e[maxm]; int cmp(node a,node b) { return a.r<b.r; } void add(int i,int w)//数组数组要从1开始 { while(i<=n) { c[i]+=w; i+=lowbit(i); } } __int64 sum(int i)//数状数组求前i个数的和 { __int64 s=0; while(i>0) { s+=c[i]; i-=lowbit(i); } return s; } int binary(int x,int num)//二分查找数组下标 { int l,r,mid; l=0;r=num-1; while(l<=r) { mid=(l+r)/2; if(a[mid]==x)return mid; if(a[mid]>x) r=mid-1; else l=mid+1; } return -1; } int main() { int T; scanf("%d",&T); while(T--) { int i,j,k,t=0; scanf("%d",&n); for(i=0;i<=n;i++) c[i]=pre[i]=0;//pre记录上一个i出现的位置 for(i=1;i<=n;i++) { scanf("%d",&b[i]); a[t++]=b[i];//离散化 } sort(a,a+t); t=unique(a,a+t)-a;//去重 scanf("%d",&m); for(i=1;i<=m;i++) { scanf("%d%d",&e[i].l,&e[i].r); e[i].id=i; } sort(e+1,e+m+1,cmp); for(i=1,j=1;i<=n;i++) { int id=binary(b[i],t); //printf("***%d\n",pre[id]); if(pre[id]) add(pre[id],-b[i]);//重复就去掉前面的,因为计算区间[a,b](b>i)段时,最多计算一次最后出现的重复值 add(i,b[i]); pre[id]=i; /* for(j=1;j<=n;j++) printf("%I64d ",c[j]); printf("\n");*/ while(j<=m&&e[j].r==i) { //printf("%I64d %I64d\n",sum(e[j].r),sum(e[j].l-1)); f[e[j].id]=sum(e[j].r)-sum(e[j].l-1); j++; } } for(i=1;i<=m;i++) printf("%I64d\n",f[i]); } } /* 离散化: ,因为0 ≤ Ai ≤ 1,000,000,000 ,很显然我们不能直接 用一个visit去判断。。 但是由于1 ≤ N ≤ 30,000 ,我们可以开一个30000的数组,然后把这些数存起来,排好序, 之后再判断一个数是否出现过的时候, 就可以用二分找到它的下标。。 对下表进行visit记录就可以了。。。 题意: 给出一个长度为N(N <= 30000)的数列,然后是一连串询问,询问数<= 100000,问给定区间内不同数字的和。 因为数字的范围较大,所以首先是对数列进行离散化,将大范围的数字映射到连续的区间。 然后一次性读入所有的区间(整数对),并且对整数对的右端点进行递增排序。这里注意,要记录下整数对读入的位置下标,用于输出 接下来按顺序枚举每个数字a[i],如果a[i]之前出现过,就将a[i]之前位置的值删除,然后在当前位置插入,当枚举的位置和区间对中某个位置相 等时执行询问操作,原理就是在要查找的范围时,正好重复位置更新到最新电 */