Necklace HDU - 3874 (线段树/树状数组 + 离线处理)

Necklace HDU - 3874 
Mery has a beautiful necklace. The necklace is made up of N magic balls. Each ball has a beautiful value. The balls with the same beautiful value look the same, so if two or more balls have the same beautiful value, we just count it once. We define the beautiful value of some interval [x,y] as F(x,y). F(x,y) is calculated as the sum of the beautiful value from the xth ball to the yth ball and the same value is ONLY COUNTED ONCE. For example, if the necklace is 1 1 1 2 3 1, we have F(1,3)=1, F(2,4)=3, F(2,6)=6. 

Now Mery thinks the necklace is too long. She plans to take some continuous part of the necklace to build a new one. She wants to know each of the beautiful value of M continuous parts of the necklace. She will give you M intervals [L,R] (1<=L<=R<=N) and you must tell her F(L,R) of them.

InputThe first line is T(T<=10), representing the number of test cases. 
  For each case, the first line is a number N,1 <=N <=50000, indicating the number of the magic balls. The second line contains N non-negative integer numbers not greater 1000000, representing the beautiful value of the N balls. The third line has a number M, 1 <=M <=200000, meaning the nunber of the queries. Each of the next M lines contains L and R, the query.OutputFor each query, output a line contains an integer number, representing the result of the query.Sample Input

2
6
1 2 3 4 3 5
3
1 2
3 5
2 6
6
1 1 1 2 3 5
3
1 1
2 4
3 5

Sample Output

3
7
14
1
3
6
题意:给n个数字,代表项链每个单位的价值。在给m组查询,代表需要查询的[l,r]中的项链价值的总和;
   项链区间价值的计算方法:区间中不同数字的和,即是该区间项链价值的总和。
做法:因为查询的区间已知,采用线段树/树状数组 + 离线查找的方法
   将需要查找的区间根据 右端点 从小到大排序。从左往右逐步把数据加入到线段树中,当达到某一查询区间的右端点时,进行一次区间和的计算。
   建立一个map数组,若某个数已经在序列中,则在最近出现的位置删除该数,这样就能保证每个数任意时刻只在线段树的中存储一次。
  (举个栗子:如样例输入中 map[1] = 1,当第二个1放入线段树的时候,需要将第一个1清除,并更新map[1] = 2,以此保证线段树中该价值仅存在一个,
   并且如果区间可以覆盖上一个map[1],那么区间必定可以覆盖现在的map[1]。)
  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<iostream>
  4 #include<algorithm>
  5 #include<map>
  6 #include<string>
  7 #include<set>
  8 #include<stack>
  9 #include<queue>
 10 using namespace std;
 11 const int maxn = 200010;
 12 typedef long long ll;
 13 struct node{
 14     int l,r;
 15     ll sum;
 16 }tree[maxn];//建立线段树
 17 ll T,n,m;
 18 ll value[maxn],ans[maxn];// ans数组储存答案,value储存每个单位项链的价值
 19 map<ll,int> mp;// 储存价值i在数组中最近出现的位置
 20 struct node2
 21 {
 22     int l,r;
 23     int index;//题目中要求的访问的顺序
 24 }q[maxn];
 25 
 26 void build(int rt,int left,int right){
 27     tree[rt].l = left;
 28     tree[rt].r = right;
 29     tree[rt].sum = 0;// 令每个根节点的值都为零,在计算的过程中再更新节点的值
 30     if(left == right){
 31         return;
 32     }else{
 33         int mid = (left + right)>>1;
 34         build(rt<<1,left,mid);
 35         build((rt<<1)|1, mid + 1, right);
 36     }
 37 }
 38 void updata(int rt,int pos,ll val){//逐步将数据放入线段树中
 39     tree[rt].sum += val;
 40     if(tree[rt].l == tree[rt].r && tree[rt].l == pos){
 41         return;
 42     }
 43     int mid = (tree[rt].l + tree[rt].r)>>1;
 44     if(pos <= mid){
 45         updata(rt<<1,pos,val);
 46     }else{
 47         updata((rt<<1)|1,pos,val);
 48     }
 49 }
 50 ll query(int rt,int left,int right){
 51     if(tree[rt].l == left && tree[rt].r == right){
 52         return tree[rt].sum;
 53     }
 54     int mid = (tree[rt].l + tree[rt].r)>>1;
 55     if(right <= mid){
 56         return query(rt<<1,left,right);
 57     }else if(left > mid){
 58         return query((rt<<1)|1 ,left ,right);
 59     }else{
 60         return query(rt<<1,left,mid) + query((rt<<1)|1 ,mid + 1 ,right);
 61     }
 62 }
 63 
 64 bool cmp(node2 & a, node2 & b){//按照查询区间的右端点从小到大排序
 65     return a.r < b.r;
 66 }
 67 int main(){
 68     scanf("%lld",&T);
 69     while(T--){
 70         scanf("%lld",&n);
 71         build(1,1,n);
 72         mp.clear();
 73         for(int i = 1 ; i <= n ; i++){
 74             scanf("%lld",&value[i]);
 75         }
 76         scanf("%lld",&m);
 77         for(int i = 1 ; i <= m ;i++){
 78                 scanf("%d %d",&q[i].l,&q[i].r);
 79             q[i].index = i;
 80         }
 81         sort(q + 1, q + 1 + m , cmp);
 82         int id = 1;//代表目前已经计算了几次题目想要查询的区间值
 83         for(int i = 1 ; i <= n ; i++){
 84 
 85 
 86             updata(1,i,value[i]);//按顺序从左到右将数组中的数据放入线段树中
 87             if(mp[value[i]]) updata(1,mp[value[i]],-value[i]); //如果价值value[i],将之前的数据进行删除,并将线段树更新
 88             mp[value[i]] = i;// 更新value[i]最新出现的位置
 89 
 90             while(id <= m && q[id].r == i){//当计算次数小于总次数,并且线段树对应下标等于某个查询区间的右端点时,进行一次查询
 91                 ans[q[id].index] = query(1,q[id].l, q[id].r);
 92                 id++;
 93             }
 94         }
 95         for(int i = 1 ; i <= m ; i++){
 96             printf("%lld\n",ans[i]);
 97         }
 98     }
 99     return 0;
100 }
线段树+离线处理做法

一个从很久以前就开始做的梦。


  

猜你喜欢

转载自www.cnblogs.com/DreamACMer/p/10840159.html
今日推荐