原题链接为:HDU-3333
题意是给一组数,给定m个区间查询,询问这个区间中不同的数的和(即所有重复出现的数只当作一个数)。
首先这是一道数据结构的题。这道题的关键在于离线化处理后用树状数组处理。把所有询问离线之后,按照右边界排序,然后依次处理。
处理的办法是依次向右扫描,假设现在扫描到i处,则首先查询i处数值d[i]之前是否出现过。若没出现过,则在树状数组中,在i处加上d[i];若出现过,假设之前出现处为j,则在树状数组中j处减去d[j],同时在i处加上d[i]。可以发现,因为i之前的所有查询均已处理过, 所以这样的处理并不会影响结果。
简单的说,思路就是用树状数组从左向右存储数,把所有数尽可能的往右存。
为了记录d[i]是否出现过,需要离线化处理。
代码如下:
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 5 struct Query 6 { 7 LL l,r; 8 bool operator < (const Query x) 9 { 10 if(r==x.r) return l<x.l; 11 return r<x.r; 12 } 13 }; 14 LL d[30010]; 15 Query q[100010]; 16 LL qq[100010]; 17 #define MAXN 100010 18 LL n,m,tree[MAXN]; 19 LL lowbit(LL x) 20 { 21 return x&(-x); 22 } 23 void add(LL k,LL num) 24 { 25 while(k<=n) 26 { 27 tree[k]+=num; 28 k+=lowbit(k); 29 } 30 } 31 LL sum(LL k) 32 { 33 LL ans=0; 34 while(k) 35 { 36 ans+=tree[k]; 37 k-=lowbit(k); 38 } 39 return ans; 40 } 41 map<LL,LL> dict; 42 LL vis[30010]; 43 LL ans[100010]; 44 bool cmp(LL a,LL b) 45 { 46 return q[a]<q[b]; 47 } 48 int main() 49 { 50 #ifdef LOCAL 51 freopen("in.txt","r",stdin); 52 #endif 53 LL t; 54 scanf("%lld",&t); 55 while(t--) 56 { 57 LL dictnum=0; 58 dict.clear(); 59 scanf("%lld",&n); 60 for(LL i=1;i<=n;i++) 61 { 62 scanf("%lld",&d[i]); 63 if(!dict.count(d[i])) dict[d[i]]=++dictnum; 64 } 65 scanf("%lld",&m); 66 for(LL i=1;i<=m;i++) scanf("%lld%lld",&q[i].l,&q[i].r); 67 for(LL i=1;i<=m;i++) qq[i]=i; 68 sort(qq+1,qq+m+1,cmp); 69 q[0].r=q[0].l=0; 70 qq[0]=0; 71 memset(tree,0,sizeof(tree)); 72 memset(vis,0,sizeof(vis)); 73 for(LL i=1;i<=m;i++) 74 { 75 for(LL j=q[qq[i-1]].r+1;j<=q[qq[i]].r;j++) 76 { 77 if(vis[dict[d[j]]]) add(vis[dict[d[j]]],-d[j]); 78 add(j,d[j]); 79 vis[dict[d[j]]]=j; 80 } 81 ans[qq[i]]=sum(q[qq[i]].r)-sum(q[qq[i]].l-1); 82 } 83 for(LL i=1;i<=m;i++) printf("%lld ",ans[i]); 84 } 85 return 0; 86 }