线段树的操作

移动信息安全的漏洞和逆向原理        程序员11月书讯,评论得书啦        Get IT技能知识库,50个领域一键直达
 

数据结构专题——线段树

标签: buildquery存储c
 65650人阅读 评论(40) 收藏 举报
本文章已收录于: 
 C语言知识库
 分类:

目录(?)[+]

线段树


转载请注明出处,谢谢!http://blog.csdn.NET/metalseed/article/details/8039326 

持续更新中···


一:线段树基本概念

1:概述

线段树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(lgN)!

性质:父亲的区间是[a,b],(c=(a+b)/2)左儿子的区间是[a,c],右儿子的区间是[c+1,b],线段树需要的空间为数组大小的四倍


2:基本操作(demo用的是查询区间最小值)

线段树的主要操作有:

(1):线段树的构造 void build(int node, int begin, int end);

主要思想是递归构造,如果当前节点记录的区间只有一个值,则直接赋值,否则递归构造左右子树,最后回溯的时候给当前节点赋值

[cpp] view plaincopy
print?
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. const int maxind = 256;  
  5. int segTree[maxind * 4 + 10];  
  6. int array[maxind];   
  7. /* 构造函数,得到线段树 */  
  8. void build(int node, int begin, int end)    
  9. {    
  10.     if (begin == end)    
  11.         segTree[node] = array[begin]; /* 只有一个元素,节点记录该单元素 */  
  12.     else    
  13.     {     
  14.         /* 递归构造左右子树 */   
  15.         build(2*node, begin, (begin+end)/2);    
  16.         build(2*node+1, (begin+end)/2+1, end);   
  17.            
  18.         /* 回溯时得到当前node节点的线段信息 */    
  19.         if (segTree[2 * node] <= segTree[2 * node + 1])    
  20.             segTree[node] = segTree[2 * node];    
  21.         else    
  22.             segTree[node] = segTree[2 * node + 1];    
  23.     }    
  24. }  
  25.   
  26. int main()  
  27. {  
  28.     array[0] = 1, array[1] = 2,array[2] = 2, array[3] = 4, array[4] = 1, array[5] = 3;  
  29.     build(1, 0, 5);  
  30.     for(int i = 1; i<=20; ++i)  
  31.      cout<< "seg"<< i << "=" <<segTree[i] <<endl;  
  32.     return 0;  
  33. }   
 此build构造成的树如图:

(2):区间查询int query(int node, int begin, int end, int left, int right);

(其中node为当前查询节点,begin,end为当前节点存储的区间,left,right为此次query所要查询的区间)

主要思想是把所要查询的区间[a,b]划分为线段树上的节点,然后将这些节点代表的区间合并起来得到所需信息

比如前面一个图中所示的树,如果询问区间是[0,2],或者询问的区间是[3,3],不难直接找到对应的节点回答这一问题。但并不是所有的提问都这么容易回答,比如[0,3],就没有哪一个节点记录了这个区间的最小值。当然,解决方法也不难找到:把[0,2][3,3]两个区间(它们在整数意义上是相连的两个区间)的最小值合并起来,也就是求这两个最小值的最小值,就能求出[0,3]范围的最小值。同理,对于其他询问的区间,也都可以找到若干个相连的区间,合并后可以得到询问的区间。

[cpp] view plaincopy
print?
  1. int query(int node, int begin, int end, int left, int right)    
  2. {   
  3.     int p1, p2;    
  4.     
  5.     /*  查询区间和要求的区间没有交集  */  
  6.     if (left > end || right < begin)    
  7.         return -1;    
  8.     
  9.     /*  if the current interval is included in  */    
  10.     /*  the query interval return segTree[node]  */  
  11.     if (begin >= left && end <= right)    
  12.         return segTree[node];    
  13.     
  14.     /*  compute the minimum position in the  */  
  15.     /*  left and right part of the interval  */   
  16.     p1 = query(2 * node, begin, (begin + end) / 2, left, right);   
  17.     p2 = query(2 * node + 1, (begin + end) / 2 + 1, end, left, right);    
  18.     
  19.     /*  return the expect value  */   
  20.     if (p1 == -1)    
  21.         return p2;    
  22.     if (p2 == -1)    
  23.         return p1;    
  24.     if (p1 <= p2)    
  25.         return  p1;    
  26.     return  p2;      
  27. }   

可见,这样的过程一定选出了尽量少的区间,它们相连后正好涵盖了整个[left,right],没有重复也没有遗漏。同时,考虑到线段树上每层的节点最多会被选取2个,一共选取的节点数也是O(log n)的,因此查询的时间复杂度也是O(log n)。

线段树并不适合所有区间查询情况,它的使用条件是“相邻的区间的信息可以被合并成两个区间的并区间的信息”。即问题是可以被分解解决的。



(3):区间或节点的更新 及 线段树的动态维护update (这是线段树核心价值所在,节点中的标记域可以解决N多种问题)

动态维护需要用到标记域,延迟标记等。

a:单节点更新

[cpp] view plaincopy
print?
  1. void Updata(int node, int begin, int end, int ind, int add)/*单节点更新*/    
  2. {    
  3.     
  4.     if( begin == end )    
  5.     {    
  6.         segTree[node] += add;    
  7.         return ;    
  8.     }    
  9.     int m = ( left + right ) >> 1;    
  10.     if(ind <= m)    
  11.         Updata(node * 2,left, m, ind, add);    
  12.     else    
  13.         Updata(node * 2 + 1, m + 1, right, ind, add);    
  14.     /*回溯更新父节点*/    
  15.     segTree[node] = min(segTree[node * 2], segTree[node * 2 + 1]);     
  16.          
  17. }   

b:区间更新(线段树中最有用的)

需要用到延迟标记,每个结点新增加一个标记,记录这个结点是否被进行了某种修改操作(这种修改操作会影响其子结点)。对于任意区间的修改,我们先按照查询的方式将其划分成线段树中的结点,然后修改这些结点的信息,并给这些结点标上代表这种修改操作的标记。在修改和查询的时候,如果我们到了一个结点p,并且决定考虑其子结点,那么我们就要看看结点p有没有标记,如果有,就要按照标记修改其子结点的信息,并且给子结点都标上相同的标记,同时消掉p的标记。(优点在于,不用将区间内的所有值都暴力更新,大大提高效率,因此区间更新是最优用的操作)

void Change来自dongxicheng.org

[cpp] view plaincopy
print?
  1. void Change(node *p, int a, int b) /* 当前考察结点为p,修改区间为(a,b]*/  
  2.    
  3. {  
  4.    
  5.   if (a <= p->Left && p->Right <= b)  
  6.    
  7.   /* 如果当前结点的区间包含在修改区间内*/  
  8.    
  9.   {  
  10.    
  11.      ...... /* 修改当前结点的信息,并标上标记*/  
  12.    
  13.      return;  
  14.    
  15.   }  
  16.    
  17.   Push_Down(p); /* 把当前结点的标记向下传递*/  
  18.    
  19.   int mid = (p->Left + p->Right) / 2; /* 计算左右子结点的分隔点 
  20.   
  21.   if (a < mid) Change(p->Lch, a, b); /* 和左孩子有交集,考察左子结点*/  
  22.    
  23.   if (b > mid) Change(p->Rch, a, b); /* 和右孩子有交集,考察右子结点*/  
  24.    
  25.   Update(p); /* 维护当前结点的信息(因为其子结点的信息可能有更改)*/  
  26.    
  27. }  



3:主要应用

(1):区间最值查询问题 (见模板1)

(2):连续区间修改或者单节点更新的动态查询问题 (见模板2)

(3):多维空间的动态查询 (见模板3)


二:典型模板

模板1:

RMQ,查询区间最值下标---min

[cpp] view plaincopy
print?
  1. #include<iostream>    
  2.   
  3. using namespace std;    
  4.     
  5. #define MAXN 100    
  6. #define MAXIND 256 //线段树节点个数    
  7.     
  8. //构建线段树,目的:得到M数组.    
  9. void build(int node, int b, int e, int M[], int A[])    
  10. {    
  11.     if (b == e)    
  12.         M[node] = b; //只有一个元素,只有一个下标    
  13.     else    
  14.     {     
  15.         build(2 * node, b, (b + e) / 2, M, A);    
  16.         build(2 * node + 1, (b + e) / 2 + 1, e, M, A);    
  17.   
  18.         if (A[M[2 * node]] <= A[M[2 * node + 1]])    
  19.             M[node] = M[2 * node];    
  20.         else    
  21.             M[node] = M[2 * node + 1];    
  22.     }    
  23. }    
  24.     
  25. //找出区间 [i, j] 上的最小值的索引    
  26. int query(int node, int b, int e, int M[], int A[], int i, int j)    
  27. {    
  28.     int p1, p2;    
  29.     
  30.     //查询区间和要求的区间没有交集    
  31.     if (i > e || j < b)    
  32.         return -1;    
  33.   
  34.     if (b >= i && e <= j)    
  35.         return M[node];    
  36.    
  37.     p1 = query(2 * node, b, (b + e) / 2, M, A, i, j);    
  38.     p2 = query(2 * node + 1, (b + e) / 2 + 1, e, M, A, i, j);    
  39.     
  40.     //return the position where the overall    
  41.     //minimum is    
  42.     if (p1 == -1)    
  43.         return M[node] = p2;    
  44.     if (p2 == -1)    
  45.         return M[node] = p1;    
  46.     if (A[p1] <= A[p2])    
  47.         return M[node] = p1;    
  48.     return M[node] = p2;    
  49.     
  50. }    
  51.     
  52.     
  53. int main()    
  54. {    
  55.     int M[MAXIND]; //下标1起才有意义,否则不是二叉树,保存下标编号节点对应区间最小值的下标.    
  56.     memset(M,-1,sizeof(M));    
  57.     int a[]={3,4,5,7,2,1,0,3,4,5};    
  58.     build(1, 0, sizeof(a)/sizeof(a[0])-1, M, a);    
  59.     cout<<query(1, 0, sizeof(a)/sizeof(a[0])-1, M, a, 0, 5)<<endl;    
  60.     return 0;    
  61. }    



模板2:

连续区间修改或者单节点更新的动态查询问题 (此模板查询区间和)

[cpp] view plaincopy
print?
  1. #include <cstdio>    
  2. #include <algorithm>    
  3. using namespace std;    
  4.      
  5. #define lson l , m , rt << 1    
  6. #define rson m + 1 , r , rt << 1 | 1   
  7. #define root 1 , N , 1   
  8. #define LL long long    
  9. const int maxn = 111111;    
  10. LL add[maxn<<2];    
  11. LL sum[maxn<<2];    
  12. void PushUp(int rt) {    
  13.     sum[rt] = sum[rt<<1] + sum[rt<<1|1];    
  14. }    
  15. void PushDown(int rt,int m) {    
  16.     if (add[rt]) {    
  17.         add[rt<<1] += add[rt];    
  18.         add[rt<<1|1] += add[rt];    
  19.         sum[rt<<1] += add[rt] * (m - (m >> 1));    
  20.         sum[rt<<1|1] += add[rt] * (m >> 1);    
  21.         add[rt] = 0;    
  22.     }    
  23. }    
  24. void build(int l,int r,int rt) {    
  25.     add[rt] = 0;    
  26.     if (l == r) {    
  27.         scanf("%lld",&sum[rt]);    
  28.         return ;    
  29.     }    
  30.     int m = (l + r) >> 1;    
  31.     build(lson);    
  32.     build(rson);    
  33.     PushUp(rt);    
  34. }    
  35. void update(int L,int R,int c,int l,int r,int rt) {    
  36.     if (L <= l && r <= R) {    
  37.         add[rt] += c;    
  38.         sum[rt] += (LL)c * (r - l + 1);    
  39.         return ;    
  40.     }    
  41.     PushDown(rt , r - l + 1);    
  42.     int m = (l + r) >> 1;    
  43.     if (L <= m) update(L , R , c , lson);    
  44.     if (m < R) update(L , R , c , rson);    
  45.     PushUp(rt);    
  46. }    
  47. LL query(int L,int R,int l,int r,int rt) {    
  48.     if (L <= l && r <= R) {    
  49.         return sum[rt];    
  50.     }    
  51.     PushDown(rt , r - l + 1);    
  52.     int m = (l + r) >> 1;    
  53.     LL ret = 0;    
  54.     if (L <= m) ret += query(L , R , lson);    
  55.     if (m < R) ret += query(L , R , rson);    
  56.     return ret;    
  57. }    
  58. int main() {    
  59.     int N , Q;    
  60.     scanf("%d%d",&N,&Q);    
  61.     build(root);    
  62.     while (Q --) {    
  63.         char op[2];    
  64.         int a , b , c;    
  65.         scanf("%s",op);    
  66.         if (op[0] == 'Q') {    
  67.             scanf("%d%d",&a,&b);    
  68.             printf("%lld\n",query(a , b ,root));    
  69.         } else {    
  70.             scanf("%d%d%d",&a,&b,&c);    
  71.             update(a , b , c , root);    
  72.         }    
  73.     }    
  74.     return 0;    
  75. }    


模板3:

多维空间的动态查询



三:练习题目

下面是hh线段树代码,典型练习哇~

在代码前先介绍一些我的线段树风格:

  • maxn是题目给的最大区间,而节点数要开4倍,确切的来说节点数要开大于maxn的最小2x的两倍
  • lson和rson分辨表示结点的左儿子和右儿子,由于每次传参数的时候都固定是这几个变量,所以可以用预定于比较方便的表示
  • 以前的写法是另外开两个个数组记录每个结点所表示的区间,其实这个区间不必保存,一边算一边传下去就行,只需要写函数的时候多两个参数,结合lson和rson的预定义可以很方便
  • PushUP(int rt)是把当前结点的信息更新到父结点
  • PushDown(int rt)是把当前结点的信息更新给儿子结点
  • rt表示当前子树的根(root),也就是当前所在的结点

整理这些题目后我觉得线段树的题目整体上可以分成以下四个部分:



单点更新:最最基础的线段树,只更新叶子节点,然后把信息用PushUP(int r)这个函数更新上来


  • hdu1166 敌兵布阵
  • 题意:O(-1)
  • 思路:O(-1)
    线段树功能:update:单点增减 query:区间求和

code:

[cpp] view plaincopy
print?
  1. #include<cstring>  
  2. #include<iostream>  
  3.   
  4. #define M 50005  
  5. #define lson l,m,rt<<1  
  6. #define rson m+1,r,rt<<1|1  
  7. /*left,right,root,middle*/  
  8.   
  9. int sum[M<<2];  
  10.   
  11. inline void PushPlus(int rt)  
  12. {  
  13.     sum[rt] = sum[rt<<1] + sum[rt<<1|1];  
  14. }  
  15.   
  16. void Build(int l, int r, int rt)  
  17. {  
  18.     if(l == r)  
  19.     {  
  20.         scanf("%d", &sum[rt]);  
  21.         return ;  
  22.     }  
  23.     int m = ( l + r )>>1;  
  24.   
  25.     Build(lson);  
  26.     Build(rson);  
  27.     PushPlus(rt);  
  28. }  
  29.   
  30. void Updata(int p, int add, int l, int r, int rt)  
  31. {  
  32.   
  33.     if( l == r )  
  34.     {  
  35.         sum[rt] += add;  
  36.         return ;  
  37.     }  
  38.     int m = ( l + r ) >> 1;  
  39.     if(p <= m)  
  40.         Updata(p, add, lson);  
  41.     else  
  42.         Updata(p, add, rson);  
  43.   
  44.     PushPlus(rt);  
  45. }  
  46.   
  47. int Query(int L,int R,int l,int r,int rt)  
  48. {  
  49.     if( L <= l && r <= R )  
  50.     {  
  51.         return sum[rt];  
  52.     }  
  53.     int m = ( l + r ) >> 1;  
  54.     int ans=0;  
  55.     if(L<=m )  
  56.         ans+=Query(L,R,lson);  
  57.     if(R>m)  
  58.         ans+=Query(L,R,rson);  
  59.   
  60.     return ans;  
  61. }  
  62. int main()  
  63. {     
  64.     int T, n, a, b;  
  65.     scanf("%d",&T);  
  66.     forint i = 1; i <= T; ++i )  
  67.     {  
  68.         printf("Case %d:\n",i);  
  69.         scanf("%d",&n);  
  70.         Build(1,n,1);  
  71.   
  72.         char op[10];  
  73.   
  74.         while( scanf("%s",op) &&op[0]!='E' )  
  75.         {  
  76.   
  77.             scanf("%d %d", &a, &b);  
  78.             if(op[0] == 'Q')  
  79.                 printf("%d\n",Query(a,b,1,n,1));  
  80.             else if(op[0] == 'S')  
  81.                 Updata(a,-b,1,n,1);  
  82.             else  
  83.                 Updata(a,b,1,n,1);  
  84.   
  85.         }  
  86.     }  
  87.     return 0;  
  88. }  

hdu1754 I Hate It
题意:O(-1)
思路:O(-1)
线段树功能:update:单点替换 query:区间最值

[cpp] view plaincopy
print?
  1. #include <cstdio>  
  2. #include <algorithm>  
  3. using namespace std;  
  4.    
  5. #define lson l , m , rt << 1  
  6. #define rson m + 1 , r , rt << 1 | 1  
  7. const int maxn = 222222;  
  8. int MAX[maxn<<2];  
  9. void PushUP(int rt) {  
  10.     MAX[rt] = max(MAX[rt<<1] , MAX[rt<<1|1]);  
  11. }  
  12. void build(int l,int r,int rt) {  
  13.     if (l == r) {  
  14.         scanf("%d",&MAX[rt]);  
  15.         return ;  
  16.     }  
  17.     int m = (l + r) >> 1;  
  18.     build(lson);  
  19.     build(rson);  
  20.     PushUP(rt);  
  21. }  
  22. void update(int p,int sc,int l,int r,int rt) {  
  23.     if (l == r) {  
  24.         MAX[rt] = sc;  
  25.         return ;  
  26.     }  
  27.     int m = (l + r) >> 1;  
  28.     if (p <= m) update(p , sc , lson);  
  29.     else update(p , sc , rson);  
  30.     PushUP(rt);  
  31. }  
  32. int query(int L,int R,int l,int r,int rt) {  
  33.     if (L <= l && r <= R) {  
  34.         return MAX[rt];  
  35.     }  
  36.     int m = (l + r) >> 1;  
  37.     int ret = 0;  
  38.     if (L <= m) ret = max(ret , query(L , R , lson));  
  39.     if (R > m) ret = max(ret , query(L , R , rson));  
  40.     return ret;  
  41. }  
  42. int main() {  
  43.     int n , m;  
  44.     while (~scanf("%d%d",&n,&m)) {  
  45.         build(1 , n , 1);  
  46.         while (m --) {  
  47.             char op[2];  
  48.             int a , b;  
  49.             scanf("%s%d%d",op,&a,&b);  
  50.             if (op[0] == 'Q') printf("%d\n",query(a , b , 1 , n , 1));  
  51.             else update(a , b , 1 , n , 1);  
  52.         }  
  53.     }  
  54.     return 0;  
  55. }  

hdu1394 Minimum Inversion Number
题意:求Inversion后的最小逆序数
思路:用O(nlogn)复杂度求出最初逆序数后,就可以用O(1)的复杂度分别递推出其他解
线段树功能:update:单点增减 query:区间求和

[cpp] view plaincopy
print?
  1. #include <cstdio>  
  2. #include <algorithm>  
  3. using namespace std;  
  4.    
  5. #define lson l , m , rt << 1  
  6. #define rson m + 1 , r , rt << 1 | 1  
  7. const int maxn = 5555;  
  8. int sum[maxn<<2];  
  9. void PushUP(int rt) {  
  10.     sum[rt] = sum[rt<<1] + sum[rt<<1|1];  
  11. }  
  12. void build(int l,int r,int rt) {  
  13.     sum[rt] = 0;  
  14.     if (l == r) return ;  
  15.     int m = (l + r) >> 1;  
  16.     build(lson);  
  17.     build(rson);  
  18. }  
  19. void update(int p,int l,int r,int rt) {  
  20.     if (l == r) {  
  21.         sum[rt] ++;  
  22.         return ;  
  23.     }  
  24.     int m = (l + r) >> 1;  
  25.     if (p <= m) update(p , lson);  
  26.     else update(p , rson);  
  27.     PushUP(rt);  
  28. }  
  29. int query(int L,int R,int l,int r,int rt) {  
  30.     if (L <= l && r <= R) {  
  31.         return sum[rt];  
  32.     }  
  33.     int m = (l + r) >> 1;  
  34.     int ret = 0;  
  35.     if (L <= m) ret += query(L , R , lson);  
  36.     if (R > m) ret += query(L , R , rson);  
  37.     return ret;  
  38. }  
  39. int x[maxn];  
  40. int main() {  
  41.     int n;  
  42.     while (~scanf("%d",&n)) {  
  43.         build(0 , n - 1 , 1);  
  44.         int sum = 0;  
  45.         for (int i = 0 ; i < n ; i ++) {  
  46.             scanf("%d",&x[i]);  
  47.             sum += query(x[i] , n - 1 , 0 , n - 1 , 1);  
  48.             update(x[i] , 0 , n - 1 , 1);  
  49.         }  
  50.         int ret = sum;  
  51.         for (int i = 0 ; i < n ; i ++) {  
  52.             sum += n - x[i] - x[i] - 1;  
  53.             ret = min(ret , sum);  
  54.         }  
  55.         printf("%d\n",ret);  
  56.     }  
  57.     return 0;  
  58. }  

hdu2795 Billboard
题意:h*w的木板,放进一些1*L的物品,求每次放空间能容纳且最上边的位子
思路:每次找到最大值的位子,然后减去L
线段树功能:query:区间求最大值的位子(直接把update的操作在query里做了)

[cpp] view plaincopy
print?
  1. #include <cstdio>  
  2. #include <algorithm>  
  3. using namespace std;  
  4.    
  5. #define lson l , m , rt << 1  
  6. #define rson m + 1 , r , rt << 1 | 1  
  7. const int maxn = 222222;  
  8. int h , w , n;  
  9. int MAX[maxn<<2];  
  10. void PushUP(int rt) {  
  11.     MAX[rt] = max(MAX[rt<<1] , MAX[rt<<1|1]);  
  12. }  
  13. void build(int l,int r,int rt) {  
  14.     MAX[rt] = w;  
  15.     if (l == r) return ;  
  16.     int m = (l + r) >> 1;  
  17.     build(lson);  
  18.     build(rson);  
  19. }  
  20. int query(int x,int l,int r,int rt) {  
  21.     if (l == r) {  
  22.         MAX[rt] -= x;  
  23.         return l;  
  24.     }  
  25.     int m = (l + r) >> 1;  
  26.     int ret = (MAX[rt<<1] >= x) ? query(x , lson) : query(x , rson);  
  27.     PushUP(rt);  
  28.     return ret;  
  29. }  
  30. int main() {  
  31.     while (~scanf("%d%d%d",&h,&w,&n)) {  
  32.         if (h > n) h = n;  
  33.         build(1 , h , 1);  
  34.         while (n --) {  
  35.             int x;  
  36.             scanf("%d",&x);  
  37.             if (MAX[1] < x) puts("-1");  
  38.             else printf("%d\n",query(x , 1 , h , 1));  
  39.         }  
  40.     }  
  41.     return 0;  
  42. }  

成段更新(通常这对初学者来说是一道坎),需要用到延迟标记(或者说懒惰标记),简单来说就是每次更新的时候不要更新到底,用延迟标记使得更新延迟到下次需要更新or询问到的时候

hdu1698 Just a Hook
题意:O(-1)
思路:O(-1)
线段树功能:update:成段替换 (由于只query一次总区间,所以可以直接输出1结点的信息)
[cpp] view plaincopy
print?
  1. #include <cstdio>  
  2. #include <algorithm>  
  3. using namespace std;  
  4.    
  5. #define lson l , m , rt << 1  
  6. #define rson m + 1 , r , rt << 1 | 1  
  7. const int maxn = 111111;  
  8. int h , w , n;  
  9. int col[maxn<<2];  
  10. int sum[maxn<<2];  
  11. void PushUp(int rt) {  
  12.     sum[rt] = sum[rt<<1] + sum[rt<<1|1];  
  13. }  
  14. void PushDown(int rt,int m) {  
  15.     if (col[rt]) {  
  16.         col[rt<<1] = col[rt<<1|1] = col[rt];  
  17.         sum[rt<<1] = (m - (m >> 1)) * col[rt];  
  18.         sum[rt<<1|1] = (m >> 1) * col[rt];  
  19.         col[rt] = 0;  
  20.     }  
  21. }  
  22. void build(int l,int r,int rt) {  
  23.     col[rt] = 0;  
  24.     sum[rt] = 1;  
  25.     if (l == r) return ;  
  26.     int m = (l + r) >> 1;  
  27.     build(lson);  
  28.     build(rson);  
  29.     PushUp(rt);  
  30. }  
  31. void update(int L,int R,int c,int l,int r,int rt) {  
  32.     if (L <= l && r <= R) {  
  33.         col[rt] = c;  
  34.         sum[rt] = c * (r - l + 1);  
  35.         return ;  
  36.     }  
  37.     PushDown(rt , r - l + 1);  
  38.     int m = (l + r) >> 1;  
  39.     if (L <= m) update(L , R , c , lson);  
  40.     if (R > m) update(L , R , c , rson);  
  41.     PushUp(rt);  
  42. }  
  43. int main() {  
  44.     int T , n , m;  
  45.     scanf("%d",&T);  
  46.     for (int cas = 1 ; cas <= T ; cas ++) {  
  47.         scanf("%d%d",&n,&m);  
  48.         build(1 , n , 1);  
  49.         while (m --) {  
  50.             int a , b , c;  
  51.             scanf("%d%d%d",&a,&b,&c);  
  52.             update(a , b , c , 1 , n , 1);  
  53.         }  
  54.         printf("Case %d: The total value of the hook is %d.\n",cas , sum[1]);  
  55.     }  
  56.     return 0;  
  57. }  

poj3468 A Simple Problem with Integers
题意:O(-1)
思路:O(-1)
线段树功能:update:成段增减 query:区间求和

[cpp] view plaincopy
print?
  1. #include <cstdio>  
  2. #include <algorithm>  
  3. using namespace std;  
  4.    
  5. #define lson l , m , rt << 1  
  6. #define rson m + 1 , r , rt << 1 | 1  
  7. #define LL long long  
  8. const int maxn = 111111;  
  9. LL add[maxn<<2];  
  10. LL sum[maxn<<2];  
  11. void PushUp(int rt) {  
  12.     sum[rt] = sum[rt<<1] + sum[rt<<1|1];  
  13. }  
  14. void PushDown(int rt,int m) {  
  15.     if (add[rt]) {  
  16.         add[rt<<1] += add[rt];  
  17.         add[rt<<1|1] += add[rt];  
  18.         sum[rt<<1] += add[rt] * (m - (m >> 1));  
  19.         sum[rt<<1|1] += add[rt] * (m >> 1);  
  20.         add[rt] = 0;  
  21.     }  
  22. }  
  23. void build(int l,int r,int rt) {  
  24.     add[rt] = 0;  
  25.     if (l == r) {  
  26.         scanf("%lld",&sum[rt]);  
  27.         return ;  
  28.     }  
  29.     int m = (l + r) >> 1;  
  30.     build(lson);  
  31.     build(rson);  
  32.     PushUp(rt);  
  33. }  
  34. void update(int L,int R,int c,int l,int r,int rt) {  
  35.     if (L <= l && r <= R) {  
  36.         add[rt] += c;  
  37.         sum[rt] += (LL)c * (r - l + 1);  
  38.         return ;  
  39.     }  
  40.     PushDown(rt , r - l + 1);  
  41.     int m = (l + r) >> 1;  
  42.     if (L <= m) update(L , R , c , lson);  
  43.     if (m < R) update(L , R , c , rson);  
  44.     PushUp(rt);  
  45. }  
  46. LL query(int L,int R,int l,int r,int rt) {  
  47.     if (L <= l && r <= R) {  
  48.         return sum[rt];  
  49.     }  
  50.     PushDown(rt , r - l + 1);  
  51.     int m = (l + r) >> 1;  
  52.     LL ret = 0;  
  53.     if (L <= m) ret += query(L , R , lson);  
  54.     if (m < R) ret += query(L , R , rson);  
  55.     return ret;  
  56. }  
  57. int main() {  
  58.     int N , Q;  
  59.     scanf("%d%d",&N,&Q);  
  60.     build(1 , N , 1);  
  61.     while (Q --) {  
  62.         char op[2];  
  63.         int a , b , c;  
  64.         scanf("%s",op);  
  65.         if (op[0] == 'Q') {  
  66.             scanf("%d%d",&a,&b);  
  67.             printf("%lld\n",query(a , b , 1 , N , 1));  
  68.         } else {  
  69.             scanf("%d%d%d",&a,&b,&c);  
  70.             update(a , b , c , 1 , N , 1);  
  71.         }  
  72.     }  
  73.     return 0;  
  74. }  

poj2528 Mayor’s posters
题意:在墙上贴海报,海报可以互相覆盖,问最后可以看见几张海报
思路:这题数据范围很大,直接搞超时+超内存,需要离散化:
离散化简单的来说就是只取我们需要的值来用,比如说区间[1000,2000],[1990,2012] 我们用不到[-∞,999][1001,1989][1991,1999][2001,2011][2013,+∞]这些值,所以我只需要1000,1990,2000,2012就够了,将其分别映射到0,1,2,3,在于复杂度就大大的降下来了
所以离散化要保存所有需要用到的值,排序后,分别映射到1~n,这样复杂度就会小很多很多
而这题的难点在于每个数字其实表示的是一个单位长度(并非一个点),这样普通的离散化会造成许多错误(包括我以前的代码,poj这题数据奇弱)
给出下面两个简单的例子应该能体现普通离散化的缺陷:
例子一:1-10 1-4 5-10
例子二:1-10 1-4 6-10
普通离散化后都变成了[1,4][1,2][3,4]
线段2覆盖了[1,2],线段3覆盖了[3,4],那么线段1是否被完全覆盖掉了呢?
例子一是完全被覆盖掉了,而例子二没有被覆盖

为了解决这种缺陷,我们可以在排序后的数组上加些处理,比如说[1,2,6,10]
如果相邻数字间距大于1的话,在其中加上任意一个数字,比如加成[1,2,3,6,7,10],然后再做线段树就好了.
线段树功能:update:成段替换 query:简单hash

[cpp] view plaincopy
print?
  1. #include <cstdio>  
  2. #include <cstring>  
  3. #include <algorithm>  
  4. using namespace std;  
  5. #define lson l , m , rt << 1  
  6. #define rson m + 1 , r , rt << 1 | 1  
  7.    
  8. const int maxn = 11111;  
  9. bool hash[maxn];  
  10. int li[maxn] , ri[maxn];  
  11. int X[maxn*3];  
  12. int col[maxn<<4];  
  13. int cnt;  
  14.    
  15. void PushDown(int rt) {  
  16.     if (col[rt] != -1) {  
  17.         col[rt<<1] = col[rt<<1|1] = col[rt];  
  18.         col[rt] = -1;  
  19.     }  
  20. }  
  21. void update(int L,int R,int c,int l,int r,int rt) {  
  22.     if (L <= l && r <= R) {  
  23.         col[rt] = c;  
  24.         return ;  
  25.     }  
  26.     PushDown(rt);  
  27.     int m = (l + r) >> 1;  
  28.     if (L <= m) update(L , R , c , lson);  
  29.     if (m < R) update(L , R , c , rson);  
  30. }  
  31. void query(int l,int r,int rt) {  
  32.     if (col[rt] != -1) {  
  33.         if (!hash[col[rt]]) cnt ++;  
  34.         hash[ col[rt] ] = true;  
  35.         return ;  
  36.     }  
  37.     if (l == r) return ;  
  38.     int m = (l + r) >> 1;  
  39.     query(lson);  
  40.     query(rson);  
  41. }  
  42. int Bin(int key,int n,int X[]) {  
  43.     int l = 0 , r = n - 1;  
  44.     while (l <= r) {  
  45.         int m = (l + r) >> 1;  
  46.         if (X[m] == key) return m;  
  47.         if (X[m] < key) l = m + 1;  
  48.         else r = m - 1;  
  49.     }  
  50.     return -1;  
  51. }  
  52. int main() {  
  53.     int T , n;  
  54.     scanf("%d",&T);  
  55.     while (T --) {  
  56.         scanf("%d",&n);  
  57.         int nn = 0;  
  58.         for (int i = 0 ; i < n ; i ++) {  
  59.             scanf("%d%d",&li[i] , &ri[i]);  
  60.             X[nn++] = li[i];  
  61.             X[nn++] = ri[i];  
  62.         }  
  63.         sort(X , X + nn);  
  64.         int m = 1;  
  65.         for (int i = 1 ; i < nn; i ++) {  
  66.             if (X[i] != X[i-1]) X[m ++] = X[i];  
  67.         }  
  68.         for (int i = m - 1 ; i > 0 ; i --) {  
  69.             if (X[i] != X[i-1] + 1) X[m ++] = X[i-1] + 1;  
  70.         }  
  71.         sort(X , X + m);  
  72.         memset(col , -1 , sizeof(col));  
  73.         for (int i = 0 ; i < n ; i ++) {  
  74.             int l = Bin(li[i] , m , X);  
  75.             int r = Bin(ri[i] , m , X);  
  76.             update(l , r , i , 0 , m , 1);  
  77.         }  
  78.         cnt = 0;  
  79.         memset(hash , false , sizeof(hash));  
  80.         query(0 , m , 1);  
  81.         printf("%d\n",cnt);  
  82.     }  
  83.     return 0;  
  84. }  

poj3225 Help with Intervals
题意:区间操作,交,并,补等
思路:
我们一个一个操作来分析:(用0和1表示是否包含区间,-1表示该区间内既有包含又有不包含)
U:把区间[l,r]覆盖成1
I:把[-∞,l)(r,∞]覆盖成0
D:把区间[l,r]覆盖成0
C:把[-∞,l)(r,∞]覆盖成0 , 且[l,r]区间0/1互换
S:[l,r]区间0/1互换

成段覆盖的操作很简单,比较特殊的就是区间0/1互换这个操作,我们可以称之为异或操作
很明显我们可以知道这个性质:当一个区间被覆盖后,不管之前有没有异或标记都没有意义了
所以当一个节点得到覆盖标记时把异或标记清空
而当一个节点得到异或标记的时候,先判断覆盖标记,如果是0或1,直接改变一下覆盖标记,不然的话改变异或标记

开区间闭区间只要数字乘以2就可以处理(偶数表示端点,奇数表示两端点间的区间)
线段树功能:update:成段替换,区间异或 query:简单hash

[cpp] view plaincopy
print?
  1. #include <cstdio>  
  2. #include <cstring>  
  3. #include <cctype>  
  4. #include <algorithm>  
  5. using namespace std;  
  6. #define lson l , m , rt << 1  
  7. #define rson m + 1 , r , rt << 1 | 1  
  8.    
  9. const int maxn = 131072;  
  10. bool hash[maxn+1];  
  11. int cover[maxn<<2];  
  12. int XOR[maxn<<2];  
  13. void FXOR(int rt) {  
  14.     if (cover[rt] != -1) cover[rt] ^= 1;  
  15.     else XOR[rt] ^= 1;  
  16. }  
  17. void PushDown(int rt) {  
  18.     if (cover[rt] != -1) {  
  19.         cover[rt<<1] = cover[rt<<1|1] = cover[rt];  
  20.         XOR[rt<<1] = XOR[rt<<1|1] = 0;  
  21.         cover[rt] = -1;  
  22.     }  
  23.     if (XOR[rt]) {  
  24.         FXOR(rt<<1);  
  25.         FXOR(rt<<1|1);  
  26.         XOR[rt] = 0;  
  27.     }  
  28. }  
  29. void update(char op,int L,int R,int l,int r,int rt) {  
  30.     if (L <= l && r <= R) {  
  31.         if (op == 'U') {  
  32.             cover[rt] = 1;  
  33.             XOR[rt] = 0;  
  34.         } else if (op == 'D') {  
  35.             cover[rt] = 0;  
  36.             XOR[rt] = 0;  
  37.         } else if (op == 'C' || op == 'S') {  
  38.             FXOR(rt);  
  39.         }  
  40.         return ;  
  41.     }  
  42.     PushDown(rt);  
  43.     int m = (l + r) >> 1;  
  44.     if (L <= m) update(op , L , R , lson);  
  45.     else if (op == 'I' || op == 'C') {  
  46.         XOR[rt<<1] = cover[rt<<1] = 0;  
  47.     }  
  48.     if (m < R) update(op , L , R , rson);  
  49.     else if (op == 'I' || op == 'C') {  
  50.         XOR[rt<<1|1] = cover[rt<<1|1] = 0;  
  51.     }  
  52. }  
  53. void query(int l,int r,int rt) {  
  54.     if (cover[rt] == 1) {  
  55.         for (int it = l ; it <= r ; it ++) {  
  56.             hash[it] = true;  
  57.         }  
  58.         return ;  
  59.     } else if (cover[rt] == 0) return ;  
  60.     if (l == r) return ;  
  61.     PushDown(rt);  
  62.     int m = (l + r) >> 1;  
  63.     query(lson);  
  64.     query(rson);  
  65. }  
  66. int main() {  
  67.     cover[1] = XOR[1] = 0;  
  68.     char op , l , r;  
  69.     int a , b;  
  70.     while ( ~scanf("%c %c%d,%d%c\n",&op , &l , &a , &b , &r) ) {  
  71.         a <<= 1 , b <<= 1;  
  72.         if (l == '(') a ++;  
  73.         if (r == ')') b --;  
  74.         if (a > b) {  
  75.             if (op == 'C' || op == 'I') {  
  76.                 cover[1] = XOR[1] = 0;  
  77.             }  
  78.         } else update(op , a , b , 0 , maxn , 1);  
  79.     }  
  80.     query(0 , maxn , 1);  
  81.     bool flag = false;  
  82.     int s = -1 , e;  
  83.     for (int i = 0 ; i <= maxn ; i ++) {  
  84.         if (hash[i]) {  
  85.             if (s == -1) s = i;  
  86.             e = i;  
  87.         } else {  
  88.             if (s != -1) {  
  89.                 if (flag) printf(" ");  
  90.                 flag = true;  
  91.                 printf("%c%d,%d%c",s&1?'(':'[' , s>>1 , (e+1)>>1 , e&1?')':']');  
  92.                 s = -1;  
  93.             }  
  94.         }  
  95.     }  
  96.     if (!flag) printf("empty set");  
  97.     puts("");  
  98.     return 0;  
  99. }  
练习
poj1436 Horizontally Visible Segments
poj2991 Crane
Another LCIS
Bracket Sequence

区间合并

这类题目会询问区间中满足条件的连续最长区间,所以PushUp的时候需要对左右儿子的区间进行合并
poj3667 Hotel
题意:1 a:询问是不是有连续长度为a的空房间,有的话住进最左边
2 a b:将[a,a+b-1]的房间清空
思路:记录区间中最长的空房间
线段树操作:update:区间替换 query:询问满足条件的最左断点
[cpp] view plaincopy
print?
  1. #include <cstdio>  
  2. #include <cstring>  
  3. #include <cctype>  
  4. #include <algorithm>  
  5. using namespace std;  
  6. #define lson l , m , rt << 1  
  7. #define rson m + 1 , r , rt << 1 | 1  
  8.    
  9. const int maxn = 55555;  
  10. int lsum[maxn<<2] , rsum[maxn<<2] , msum[maxn<<2];  
  11. int cover[maxn<<2];  
  12.    
  13. void PushDown(int rt,int m) {  
  14.     if (cover[rt] != -1) {  
  15.         cover[rt<<1] = cover[rt<<1|1] = cover[rt];  
  16.         msum[rt<<1] = lsum[rt<<1] = rsum[rt<<1] = cover[rt] ? 0 : m - (m >> 1);  
  17.         msum[rt<<1|1] = lsum[rt<<1|1] = rsum[rt<<1|1] = cover[rt] ? 0 : (m >> 1);  
  18.         cover[rt] = -1;  
  19.     }  
  20. }  
  21. void PushUp(int rt,int m) {  
  22.     lsum[rt] = lsum[rt<<1];  
  23.     rsum[rt] = rsum[rt<<1|1];  
  24.     if (lsum[rt] == m - (m >> 1)) lsum[rt] += lsum[rt<<1|1];  
  25.     if (rsum[rt] == (m >> 1)) rsum[rt] += rsum[rt<<1];  
  26.     msum[rt] = max(lsum[rt<<1|1] + rsum[rt<<1] , max(msum[rt<<1] , msum[rt<<1|1]));  
  27. }  
  28. void build(int l,int r,int rt) {  
  29.     msum[rt] = lsum[rt] = rsum[rt] = r - l + 1;  
  30.     cover[rt] = -1;  
  31.     if (l == r) return ;  
  32.     int m = (l + r) >> 1;  
  33.     build(lson);  
  34.     build(rson);  
  35. }  
  36. void update(int L,int R,int c,int l,int r,int rt) {  
  37.     if (L <= l && r <= R) {  
  38.         msum[rt] = lsum[rt] = rsum[rt] = c ? 0 : r - l + 1;  
  39.         cover[rt] = c;  
  40.         return ;  
  41.     }  
  42.     PushDown(rt , r - l + 1);  
  43.     int m = (l + r) >> 1;  
  44.     if (L <= m) update(L , R , c , lson);  
  45.     if (m < R) update(L , R , c , rson);  
  46.     PushUp(rt , r - l + 1);  
  47. }  
  48. int query(int w,int l,int r,int rt) {  
  49.     if (l == r) return l;  
  50.     PushDown(rt , r - l + 1);  
  51.     int m = (l + r) >> 1;  
  52.     if (msum[rt<<1] >= w) return query(w , lson);  
  53.     else if (rsum[rt<<1] + lsum[rt<<1|1] >= w) return m - rsum[rt<<1] + 1;  
  54.     return query(w , rson);  
  55. }  
  56. int main() {  
  57.     int n , m;  
  58.     scanf("%d%d",&n,&m);  
  59.     build(1 , n , 1);  
  60.     while (m --) {  
  61.         int op , a , b;  
  62.         scanf("%d",&op);  
  63.         if (op == 1) {  
  64.             scanf("%d",&a);  
  65.             if (msum[1] < a) puts("0");  
  66.             else {  
  67.                 int p = query(a , 1 , n , 1);  
  68.                 printf("%d\n",p);  
  69.                 update(p , p + a - 1 , 1 , 1 , n , 1);  
  70.             }  
  71.         } else {  
  72.             scanf("%d%d",&a,&b);  
  73.             update(a , a + b - 1 , 0 , 1 , n , 1);  
  74.         }  
  75.     }  
  76.     return 0;  
  77. }  

练习
hdu3308 LCIS
hdu3397 Sequence operation
hdu2871 Memory Control
hdu1540 Tunnel Warfare
CF46-D Parking Lot

扫描线

这类题目需要将一些操作排序,然后从左到右用一根扫描线(当然是在我们脑子里)扫过去
最典型的就是矩形面积并,周长并等题

hdu1542 Atlantis
题意:矩形面积并
思路:浮点数先要离散化;然后把矩形分成两条边,上边和下边,对横轴建树,然后从下到上扫描上去,用cnt表示该区间下边比上边多几个,sum代表该区间内被覆盖的线段的长度总和
这里线段树的一个结点并非是线段的一个端点,而是该端点和下一个端点间的线段,所以题目中r+1,r-1的地方可以自己好好的琢磨一下
线段树操作:update:区间增减 query:直接取根节点的值
[cpp] view plaincopy
print?
  1. #include <cstdio>  
  2. #include <cstring>  
  3. #include <cctype>  
  4. #include <algorithm>  
  5. using namespace std;  
  6. #define lson l , m , rt << 1  
  7. #define rson m + 1 , r , rt << 1 | 1  
  8.    
  9. const int maxn = 2222;  
  10. int cnt[maxn << 2];  
  11. double sum[maxn << 2];  
  12. double X[maxn];  
  13. struct Seg {  
  14.     double h , l , r;  
  15.     int s;  
  16.     Seg(){}  
  17.     Seg(double a,double b,double c,int d) : l(a) , r(b) , h(c) , s(d) {}  
  18.     bool operator < (const Seg &cmp) const {  
  19.         return h < cmp.h;  
  20.     }  
  21. }ss[maxn];  
  22. void PushUp(int rt,int l,int r) {  
  23.     if (cnt[rt]) sum[rt] = X[r+1] - X[l];  
  24.     else if (l == r) sum[rt] = 0;  
  25.     else sum[rt] = sum[rt<<1] + sum[rt<<1|1];  
  26. }  
  27. void update(int L,int R,int c,int l,int r,int rt) {  
  28.     if (L <= l && r <= R) {  
  29.         cnt[rt] += c;  
  30.         PushUp(rt , l , r);  
  31.         return ;  
  32.     }  
  33.     int m = (l + r) >> 1;  
  34.     if (L <= m) update(L , R , c , lson);  
  35.     if (m < R) update(L , R , c , rson);  
  36.     PushUp(rt , l , r);  
  37. }  
  38. int Bin(double key,int n,double X[]) {  
  39.     int l = 0 , r = n - 1;  
  40.     while (l <= r) {  
  41.         int m = (l + r) >> 1;  
  42.         if (X[m] == key) return m;  
  43.         if (X[m] < key) l = m + 1;  
  44.         else r = m - 1;  
  45.     }  
  46.     return -1;  
  47. }  
  48. int main() {  
  49.     int n , cas = 1;  
  50.     while (~scanf("%d",&n) && n) {  
  51.         int m = 0;  
  52.         while (n --) {  
  53.             double a , b , c , d;  
  54.             scanf("%lf%lf%lf%lf",&a,&b,&c,&d);  
  55.             X[m] = a;  
  56.             ss[m++] = Seg(a , c , b , 1);  
  57.             X[m] = c;  
  58.             ss[m++] = Seg(a , c , d , -1);  
  59.         }  
  60.         sort(X , X + m);  
  61.         sort(ss , ss + m);  
  62.         int k = 1;  
  63.         for (int i = 1 ; i < m ; i ++) {  
  64.             if (X[i] != X[i-1]) X[k++] = X[i];  
  65.         }  
  66.         memset(cnt , 0 , sizeof(cnt));  
  67.         memset(sum , 0 , sizeof(sum));  
  68.         double ret = 0;  
  69.         for (int i = 0 ; i < m - 1 ; i ++) {  
  70.             int l = Bin(ss[i].l , k , X);  
  71.             int r = Bin(ss[i].r , k , X) - 1;  
  72.             if (l <= r) update(l , r , ss[i].s , 0 , k - 1, 1);  
  73.             ret += sum[1] * (ss[i+1].h - ss[i].h);  
  74.         }  
  75.         printf("Test case #%d\nTotal explored area: %.2lf\n\n",cas++ , ret);  
  76.     }  
  77.     return 0;  
  78. }  

hdu1828 Picture
题意:矩形周长并
思路:与面积不同的地方是还要记录竖的边有几个(numseg记录),并且当边界重合的时候需要合并(用lbd和rbd表示边界来辅助)
线段树操作:update:区间增减 query:直接取根节点的值

[cpp] view plaincopy
print?
  1. #include <cstdio>  
  2. #include <cstring>  
  3. #include <cctype>  
  4. #include <algorithm>  
  5. using namespace std;  
  6. #define lson l , m , rt << 1  
  7. #define rson m + 1 , r , rt << 1 | 1  
  8.    
  9. const int maxn = 22222;  
  10. struct Seg{  
  11.     int l , r , h , s;  
  12.     Seg() {}  
  13.     Seg(int a,int b,int c,int d):l(a) , r(b) , h(c) , s(d) {}  
  14.     bool operator < (const Seg &cmp) const {  
  15.         if (h == cmp.h) return s > cmp.s;  
  16.         return h < cmp.h;  
  17.     }  
  18. }ss[maxn];  
  19. bool lbd[maxn<<2] , rbd[maxn<<2];  
  20. int numseg[maxn<<2];  
  21. int cnt[maxn<<2];  
  22. int len[maxn<<2];  
  23. void PushUP(int rt,int l,int r) {  
  24.     if (cnt[rt]) {  
  25.         lbd[rt] = rbd[rt] = 1;  
  26.         len[rt] = r - l + 1;  
  27.         numseg[rt] = 2;  
  28.     } else if (l == r) {  
  29.         len[rt] = numseg[rt] = lbd[rt] = rbd[rt] = 0;  
  30.     } else {  
  31.         lbd[rt] = lbd[rt<<1];  
  32.         rbd[rt] = rbd[rt<<1|1];  
  33.         len[rt] = len[rt<<1] + len[rt<<1|1];  
  34.         numseg[rt] = numseg[rt<<1] + numseg[rt<<1|1];  
  35.         if (lbd[rt<<1|1] && rbd[rt<<1]) numseg[rt] -= 2;//两条线重合  
  36.     }  
  37. }  
  38. void update(int L,int R,int c,int l,int r,int rt) {  
  39.     if (L <= l && r <= R) {  
  40.         cnt[rt] += c;  
  41.         PushUP(rt , l , r);  
  42.         return ;  
  43.     }  
  44.     int m = (l + r) >> 1;  
  45.     if (L <= m) update(L , R , c , lson);  
  46.     if (m < R) update(L , R , c , rson);  
  47.     PushUP(rt , l , r);  
  48. }  
  49. int main() {  
  50.     int n;  
  51.     while (~scanf("%d",&n)) {  
  52.         int m = 0;  
  53.         int lbd = 10000, rbd = -10000;  
  54.         for (int i = 0 ; i < n ; i ++) {  
  55.             int a , b , c , d;  
  56.             scanf("%d%d%d%d",&a,&b,&c,&d);  
  57.             lbd = min(lbd , a);  
  58.             rbd = max(rbd , c);  
  59.             ss[m++] = Seg(a , c , b , 1);  
  60.             ss[m++] = Seg(a , c , d , -1);  
  61.         }  
  62.         sort(ss , ss + m);  
  63.         int ret = 0 , last = 0;  
  64.         for (int i = 0 ; i < m ; i ++) {  
  65.             if (ss[i].l < ss[i].r) update(ss[i].l , ss[i].r - 1 , ss[i].s , lbd , rbd - 1 , 1);  
  66.             ret += numseg[1] * (ss[i+1].h - ss[i].h);  
  67.             ret += abs(len[1] - last);  
  68.             last = len[1];  
  69.         }  
  70.         printf("%d\n",ret);  
  71.     }  
  72.     return 0;  
  73. }  

练习
hdu3265 Posters
hdu3642 Get The Treasury
poj2482 Stars in Your Window
poj2464 Brownie Points II
hdu3255 Farming 
ural1707 Hypnotoad’s Secret
uva11983 Weird Advertisement

多颗线段树问题

此类题目主用特点是区间不连续,有一定规律间隔,用多棵树表示不同的偏移区间
hdu 4288 coder
题意:
维护一个有序数列{An},有三种操作:
1、添加一个元素。
2、删除一个元素。
3、求数列中下标%5 = 3的值的和。

由于有删除和添加操作,所以离线离散操作,节点中cnt存储区间中有几个数,sum存储偏移和
[cpp] view plaincopy
print?
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<cstring>  
  4. #include<algorithm>  
  5. using namespace std;  
  6. const int maxn=100002;  
  7.   
  8. #define lson l , m , rt << 1    
  9. #define rson m + 1 , r , rt << 1 | 1   
  10.   
  11. __int64 sum[maxn<<2][6];  
  12. int cnt[maxn << 2];  
  13.   
  14. char op[maxn][20];  
  15. int a[maxn];  
  16.   
  17. int X[maxn];  
  18.   
  19. void PushUp(int rt)  
  20. {  
  21.     cnt[rt] = cnt[rt<<1] + cnt[rt<<1|1];  
  22.       
  23.     int offset = cnt[rt<<1];  
  24.     for(int i = 0; i < 5; ++i)  
  25.     {  
  26.         sum[rt][i] = sum[rt<<1][i];  
  27.     }  
  28.     for(int i = 0; i < 5; ++i)  
  29.     {  
  30.         sum[rt][(i + offset) % 5] += sum[rt<<1|1][i];  
  31.     }  
  32. }  
  33.   
  34. void Build(int l, int r, int rt)    
  35. {   /*此题Build完全可以用一个memset代替*/  
  36.     cnt[rt] = 0;  
  37.     for(int i = 0; i < 5; ++i)   sum[rt][i] = 0;  
  38.     if( l == r ) return;  
  39.     int m = ( l + r )>>1;      
  40.     Build(lson);    
  41.     Build(rson);     
  42. }   
  43.   
  44. void Updata(int p, int op, int l, int r, int rt)    
  45. {     
  46.     if( l == r )    
  47.     {    
  48.         cnt[rt] = op;   
  49.         sum[rt][1] = op * X[l-1];   
  50.         return ;    
  51.     }    
  52.     int m = ( l + r ) >> 1;    
  53.     if(p <= m)    
  54.         Updata(p, op, lson);    
  55.     else    
  56.         Updata(p, op, rson);    
  57.     
  58.     PushUp(rt);    
  59. }   
  60.   
  61. int main()  
  62. {  
  63.     int n;  
  64.     while(scanf("%d", &n) != EOF)  
  65.     {  
  66.         int nn = 0;  
  67.         for(int i = 0; i < n; ++i)  
  68.         {  
  69.             scanf("%s", &op[i]);  
  70.               
  71.             if(op[i][0] != 's')  
  72.             {  
  73.                 scanf("%d", &a[i]);  
  74.                 if(op[i][0] == 'a')  
  75.                 {  
  76.                     X[nn++] = a[i];  
  77.                 }  
  78.             }  
  79.         }  
  80.           
  81.         sort(X,X+nn);/*unique前必须sort*/  
  82.         nn = unique(X, X + nn) - X; /*去重并得到总数*/  
  83.           
  84.         Build(1, nn, 1);  
  85.           
  86.         for(int i = 0; i < n; ++i)  
  87.         {  
  88.             int pos = upper_bound(X, X+nn, a[i]) - X; /* hash */   
  89.             if(op[i][0] == 'a')  
  90.             {  
  91.                 Updata(pos, 1, 1, nn, 1);  
  92.             }  
  93.             else if(op[i][0] == 'd')  
  94.             {  
  95.                 Updata(pos, 0, 1, nn, 1);  
  96.             }  
  97.             else printf("%I64d\n",sum[1][3]);  
  98.         }  
  99.     }  
  100.     return 0;  
  101. }  

2:hdu 4267 A simple problem with integers
题目:给出n个数,每次将一段区间内满足(i-l)%k==0  (r>=i>=l) 的数ai增加c, 最后单点查询。
这种题目更新的区间是零散的,如果可以通过某种方式让离散的都变得连续,那么问题就可以用线段树完美解决。解决方式一般也是固定的,那就是利用题意维护多颗线段树。此题虚维护55颗,更新最终确定在一颗上,查询则将查询点被包含的树全部叠加。
[cpp] view plaincopy
print?
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<cstring>  
  4. #include<cmath>  
  5. #include<algorithm>  
  6. #include<set>  
  7. #include<vector>  
  8. #include<string>  
  9. #include<map>  
  10. #define eps 1e-7  
  11. #define LL long long  
  12. #define N 500005  
  13. #define zero(a) fabs(a)<eps  
  14. #define lson step<<1  
  15. #define rson step<<1|1  
  16. #define MOD 1234567891  
  17. #define pb(a) push_back(a)  
  18. using namespace std;  
  19. struct Node{  
  20.     int left,right,add[55],sum;  
  21.     int mid(){return (left+right)/2;}  
  22. }L[4*N];  
  23. int a[N],n,b[11][11];  
  24. void Bulid(int step ,int l,int r){  
  25.     L[step].left=l;  
  26.     L[step].right=r;  
  27.     L[step].sum=0;  
  28.     memset(L[step].add,0,sizeof(L[step].add));  
  29.     if(l==r) return ;  
  30.     Bulid(lson,l,L[step].mid());  
  31.     Bulid(rson,L[step].mid()+1,r);  
  32. }  
  33. void push_down(int step){  
  34.     if(L[step].sum){  
  35.         L[lson].sum+=L[step].sum;  
  36.         L[rson].sum+=L[step].sum;  
  37.         L[step].sum=0;  
  38.         for(int i=0;i<55;i++){  
  39.                 L[lson].add[i]+=L[step].add[i];  
  40.                 L[rson].add[i]+=L[step].add[i];  
  41.                 L[step].add[i]=0;  
  42.         }  
  43.     }  
  44. }  
  45. void update(int step,int l,int r,int num,int i,int j){  
  46.     if(L[step].left==l&&L[step].right==r){  
  47.         L[step].sum+=num;  
  48.         L[step].add[b[i][j]]+=num;  
  49.         return;  
  50.     }  
  51.     push_down(step);  
  52.     if(r<=L[step].mid()) update(lson,l,r,num,i,j);  
  53.     else if(l>L[step].mid()) update(rson,l,r,num,i,j);  
  54.     else {  
  55.         update(lson,l,L[step].mid(),num,i,j);  
  56.         update(rson,L[step].mid()+1,r,num,i,j);  
  57.     }  
  58. }  
  59. int query(int step,int pos){  
  60.     if(L[step].left==L[step].right){  
  61.         int tmp=0;  
  62.         for(int i=1;i<=10;i++)  tmp+=L[step].add[b[i][pos%i]];  
  63.         return a[L[step].left]+tmp;  
  64.     }  
  65.     push_down(step);  
  66.     if(pos<=L[step].mid()) return query(lson,pos);  
  67.     else return query(rson,pos);  
  68. }  
  69. int main(){  
  70.     int cnt=0;  
  71.     for(int i=1;i<=10;i++) for(int j=0;j<i;j++) b[i][j]=cnt++;  
  72.     while(scanf("%d",&n)!=EOF){  
  73.         for(int i=1;i<=n;i++) scanf("%d",&a[i]);  
  74.         Bulid(1,1,n);  
  75.         int q,d;  
  76.         scanf("%d",&q);  
  77.         while(q--){  
  78.             int k,l,r,m;  
  79.             scanf("%d",&k);  
  80.             if(k==2){  
  81.                 scanf("%d",&m);  
  82.                 printf("%d\n",query(1,m));  
  83.             }  
  84.             else{  
  85.                 scanf("%d%d%d%d",&l,&r,&d,&m);  
  86.                 update(1,l,r,m,d,l%d);  
  87.             }  
  88.         }  
  89.     }  
  90.     return 0;  
  91. }  




线段树与其他结合练习(欢迎大家补充):

  • hdu3954 Level up
  • hdu4027 Can you answer these queries?
  • hdu3333 Turing Tree
  • hdu3874 Necklace
  • hdu3016 Man Down
  • hdu3340 Rain in ACStar
  • zju3511 Cake Robbery
  • UESTC1558 Charitable Exchange
  • CF85-D Sum of Medians
  • spojGSS2 Can you answer these queries II


51
1

我的同类文章

  • 天马行空的ACM现场赛回顾2013-11-13
  • hdu 4711 Weather概率dp2013-09-20
  • hdu 4722 GoodNumbers2013-09-19
  • 动态规划-各种整数划分2013-09-01
  • Three Swaps DFS2013-08-31
  • 单点更新线段树 RMQ2013-08-27
  • Simple Template2013-10-20
  • 数据结构---各种树模板 持续更新···2013-09-20
  • hdu 4712 Hamming distance2013-09-19
  • Book of Evil 树双向DFS2013-08-31
  • 双向DFS模板题2013-08-30
更多文章

参考知识库

img

.NET知识库

猜你在找
顾荣:开源大数据存储系统Alluxio(原Tachyon)的原理分析与案例简介
数据结构(C版)
Ceph—分布式存储系统的另一个选择
Docker与容器服务扩展机制 - 存储
数据结构基础系列(5):数组与广义表
cdoj 2015数据结构专题M - 秋实大哥与线段树
CUGB专题训练之数据结构B - Count Color 线段树区间更新
数据结构线段树单点更新
数据结构线段树详解超详细
活用各种数据结构线段树篇
查看评论
26楼 消磨时间 2016-08-10 16:19发表 [回复]
请问,为什么要用四倍的空间。谢谢你。
25楼 bighero4 2016-06-26 21:26发表 [回复]
看明白了,那个build的例子,每个区间节点的value代表此区间(在数组array中是下标范围)上的的数组array的最小值。
24楼 bighero4 2016-06-26 21:15发表 [回复]
把所有错误纠正一下吧。比如说:线段树并非完全二叉树。
有些地方做一下解释,比如Build中的Value是什么意思?
23楼 Sci_M3 2016-06-08 11:42发表 [回复]
模板一是有错的,顺便这个错就是您自己跑的样例的问题...
22楼 zlqdhrdhrdhr 2015-11-29 09:27发表 [回复]
博主第3块代码有个小错误
那个left跟right应该是begin和end吧 ^_^
Re: wyj__jenny 2016-02-04 11:39发表 [回复]
回复zlqdhrdhrdhr:同意
21楼 清风小竹 2015-11-03 16:12发表 [回复]
楼主的博客写的很好!赞一个
20楼 liangshanxiaohan 2015-10-28 21:01发表 [回复]
楼主你好,我想请问一下,你是否接触过range tree,能不能给一段range tree建树的代码
19楼 Lormons 2015-09-27 11:43发表 [回复]
不是完全二叉树吧...~~
18楼 LuckyqXd 2015-08-22 23:22发表 [回复]
线段树可以不为完全二叉树。。。。
17楼 bestcoder_judge 2015-08-21 15:44发表 [回复]
不错不错,线段树估计你没问题了!
16楼 Triangle_libing 2015-08-12 10:28发表 [回复]
这跟编译器有什么关系
15楼 Anger_Coder 2015-07-03 18:46发表 [回复]
有一个点需要注意,mid的求法,不要(begin+end)/2这个会溢出,要begin + (end - begin) >> 1 这样比较好
Re: glassMonster 2015-11-17 19:15发表 [回复]
回复Anger_Coder:不过需要注意一点,移位运算符的优先级低,在这里就比“+”低,最后的结果就是end>>1,会使程序运行出错,所以最好再加括号begin+((end-begin)>>1)
14楼 小飞哇咔咔 2015-04-01 12:01发表 [回复]
mark
13楼 ouweiqi 2015-03-30 11:13发表 [回复]
错误百出,还好意思贴出来
Re: Echizens 2015-08-20 15:16发表 [回复]
回复ouweiqi:至少人家很谦虚。所以有意见提出来,不要BB。
12楼 楼上小宇 2015-01-27 23:43发表 [回复]
受教了。。。很详细
11楼 MetalSeed 2014-11-12 16:09发表 [回复]
228003430
10楼 累了泪了funny 2014-07-25 20:53发表 [回复]
为什么都不喜欢定义结构体呢,表示用数组模拟,看不懂,不要嘲笑我,我是菜鸟
Re: MetalSeed 2014-11-07 09:46发表 [回复]
回复u011422701:那个时候强迫症,数组写起来方便一些,速度稍微快一点
9楼 MetalSeed 2013-01-01 23:50发表 [回复]
http://blog.csdn.net/shiqi_614/article/details/8228102
8楼 GodHunter47 2012-12-31 15:16发表 [回复]
弱弱的屌丝一枚,不过勉为其难的给你顶一下
Re: MetalSeed 2013-01-01 23:47发表 [回复]
回复GodHunter47:```` ~~ 呼呼 谢谢大神
7楼 Zr777111 2012-10-12 15:11发表 [回复]
高深。。。
6楼 wd339092886 2012-10-12 14:09发表 [回复]
不行不行,我还是倾向于编程。。
5楼 dyx心心 2012-10-12 11:32发表 [回复]
楼主是搞ACM的?
Re: MetalSeed 2012-10-12 23:38发表 [回复]
回复dyx404514:ACM弱菜一枚.. 正在努力中..
Re: dyx心心 2012-11-19 09:37发表 [回复]
回复MetalSeed:我感觉最后那道题,hdu4267那个sum就是个标记而已,不需要
L[step].sum+=num; 只要 L[step].sum=1,就行了吧
4楼 c_006 2012-10-11 11:31发表 [回复]
mark一下。楼主的编译软件是什么的?这么花花绿绿的vs2010?
Re: mz31098 2015-08-22 03:10发表 [回复]
回复c_006:看着像sublime
Re: hellosijian 2012-10-11 19:46发表 [回复]
回复c_006:应该是vim
Re: hello_world_ww 2013-01-26 09:34发表 [回复]
回复hellosijian:GCC/G++
3楼 jiangkaii 2012-10-11 07:32发表 [回复]
进首页了!!
Re: MetalSeed 2012-10-11 10:51发表 [回复]
回复jiangkaii:是蒋凯么。。。
Re: jiangkaii 2012-10-12 01:51发表 [回复]
回复MetalSeed:是的,
2楼 pl___ 2012-10-09 16:16发表 [回复]
手动实现std::map ?
Re: MetalSeed 2012-10-09 17:36发表 [回复]
回复pl___:非也。
map是用红黑树实现的,特点是插入时按照键值自动排序
1楼 Dstnoe 2012-10-08 23:22发表 [回复]
请问,线段树和B树在应用上有什么特别之处?
Re: MetalSeed 2012-10-09 17:38发表 [回复]
回复d1x2p3:不好意思..
B树只有耳闻...
没详细研究过... 
还得努力呢
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
核心技术类目
全部主题 Hadoop AWS 移动游戏 Java Android iOS Swift 智能硬件 Docker OpenStack VPN Spark ERP IE10Eclipse CRM JavaScript 数据库 Ubuntu NFC WAP jQuery BI HTML5 Spring Apache .NET API HTML SDK IISFedora XML LBS Unity Splashtop UML components Windows Mobile Rails QEMU KDE Cassandra CloudStack FTCcoremail OPhone CouchBase 云计算 iOS6 Rackspace Web App SpringSide Maemo Compuware 大数据 aptech PerlTornado Ruby Hibernate ThinkPHP HBase Pure Solr Angular Cloud Foundry Redis Scala Django Bootstrap
  • 个人资料
  •  
    MetalSeed
     
    • 访问:377640次
    • 积分:4504
    • 等级: 
    • 排名:第4894名
    • 原创:109篇
    • 转载:22篇
    • 译文:2篇
    • 评论:154条
  • 文章分类
  • 处理器Ambarella(1)
  • 嵌入式Android(5)
  • 嵌入式linux(20)
  • STM32笔记(14)
  • ACM回忆(47)
  • MCU相关(27)
  • 随笔 小记(2)
  • 东西 小站(6)
  • 文章存档
    • 2016年07月(1)
    • 2015年06月(1)
    • 2015年05月(1)
    • 2015年04月(3)
    • 2015年03月(4)
      展开
  • 阅读排行
  • 数据结构专题——线段树(65567)
  • 主席树介绍(26904)
  • NE555 + CD4017流水灯(21772)
  • Android蓝牙串口通信模板及demo,trick(17010)
  • 两款主流摄像头OV7620与OV7670 By Demok(15269)
  • 锁存器使用总结(9929)
  • 各种音频视频编码方法(6842)
  • D/A与A/D转换器(6457)
  • STM32F407 Discovery uart1串口通信(6088)
  • 基于51的爱心流水灯源码(5203)
  • 评论排行
  • 数据结构专题——线段树(40)
  • 主席树介绍(14)
  • Android蓝牙串口通信模板及demo,trick(11)
  • 天马行空的ACM现场赛回顾(8)
  • 51单片机进阶(6)
  • 51单片机导论(6)
  • STM32F051 IAP源码分享(6)
  • 基于51的爱心流水灯源码(5)
  • 基于RS485的简单现场总线通信系统设计-南邮自动化课程设计(4)
  • C代码优化方案(4)
  • 推荐文章
    • * 程序员10月书讯,评论得书
    • * Android中Xposed框架篇---修改系统位置信息实现自身隐藏功能
    • * Chromium插件(Plugin)模块(Module)加载过程分析
    • * Android TV开发总结--构建一个TV app的直播节目实例
    • * 架构设计:系统存储--MySQL简单主从方案及暴露的问题
  • 最新评论
移动信息安全的漏洞和逆向原理        程序员11月书讯,评论得书啦        Get IT技能知识库,50个领域一键直达
 

数据结构专题——线段树

标签: buildquery存储c
 65650人阅读 评论(40) 收藏 举报
本文章已收录于: 
 C语言知识库
 分类:

目录(?)[+]

线段树


转载请注明出处,谢谢!http://blog.csdn.NET/metalseed/article/details/8039326 

持续更新中···


一:线段树基本概念

1:概述

线段树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(lgN)!

性质:父亲的区间是[a,b],(c=(a+b)/2)左儿子的区间是[a,c],右儿子的区间是[c+1,b],线段树需要的空间为数组大小的四倍


2:基本操作(demo用的是查询区间最小值)

线段树的主要操作有:

(1):线段树的构造 void build(int node, int begin, int end);

主要思想是递归构造,如果当前节点记录的区间只有一个值,则直接赋值,否则递归构造左右子树,最后回溯的时候给当前节点赋值

[cpp] view plaincopy
print?
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. const int maxind = 256;  
  5. int segTree[maxind * 4 + 10];  
  6. int array[maxind];   
  7. /* 构造函数,得到线段树 */  
  8. void build(int node, int begin, int end)    
  9. {    
  10.     if (begin == end)    
  11.         segTree[node] = array[begin]; /* 只有一个元素,节点记录该单元素 */  
  12.     else    
  13.     {     
  14.         /* 递归构造左右子树 */   
  15.         build(2*node, begin, (begin+end)/2);    
  16.         build(2*node+1, (begin+end)/2+1, end);   
  17.            
  18.         /* 回溯时得到当前node节点的线段信息 */    
  19.         if (segTree[2 * node] <= segTree[2 * node + 1])    
  20.             segTree[node] = segTree[2 * node];    
  21.         else    
  22.             segTree[node] = segTree[2 * node + 1];    
  23.     }    
  24. }  
  25.   
  26. int main()  
  27. {  
  28.     array[0] = 1, array[1] = 2,array[2] = 2, array[3] = 4, array[4] = 1, array[5] = 3;  
  29.     build(1, 0, 5);  
  30.     for(int i = 1; i<=20; ++i)  
  31.      cout<< "seg"<< i << "=" <<segTree[i] <<endl;  
  32.     return 0;  
  33. }   
 此build构造成的树如图:

(2):区间查询int query(int node, int begin, int end, int left, int right);

(其中node为当前查询节点,begin,end为当前节点存储的区间,left,right为此次query所要查询的区间)

主要思想是把所要查询的区间[a,b]划分为线段树上的节点,然后将这些节点代表的区间合并起来得到所需信息

比如前面一个图中所示的树,如果询问区间是[0,2],或者询问的区间是[3,3],不难直接找到对应的节点回答这一问题。但并不是所有的提问都这么容易回答,比如[0,3],就没有哪一个节点记录了这个区间的最小值。当然,解决方法也不难找到:把[0,2][3,3]两个区间(它们在整数意义上是相连的两个区间)的最小值合并起来,也就是求这两个最小值的最小值,就能求出[0,3]范围的最小值。同理,对于其他询问的区间,也都可以找到若干个相连的区间,合并后可以得到询问的区间。

[cpp] view plaincopy
print?
  1. int query(int node, int begin, int end, int left, int right)    
  2. {   
  3.     int p1, p2;    
  4.     
  5.     /*  查询区间和要求的区间没有交集  */  
  6.     if (left > end || right < begin)    
  7.         return -1;    
  8.     
  9.     /*  if the current interval is included in  */    
  10.     /*  the query interval return segTree[node]  */  
  11.     if (begin >= left && end <= right)    
  12.         return segTree[node];    
  13.     
  14.     /*  compute the minimum position in the  */  
  15.     /*  left and right part of the interval  */   
  16.     p1 = query(2 * node, begin, (begin + end) / 2, left, right);   
  17.     p2 = query(2 * node + 1, (begin + end) / 2 + 1, end, left, right);    
  18.     
  19.     /*  return the expect value  */   
  20.     if (p1 == -1)    
  21.         return p2;    
  22.     if (p2 == -1)    
  23.         return p1;    
  24.     if (p1 <= p2)    
  25.         return  p1;    
  26.     return  p2;      
  27. }   

可见,这样的过程一定选出了尽量少的区间,它们相连后正好涵盖了整个[left,right],没有重复也没有遗漏。同时,考虑到线段树上每层的节点最多会被选取2个,一共选取的节点数也是O(log n)的,因此查询的时间复杂度也是O(log n)。

线段树并不适合所有区间查询情况,它的使用条件是“相邻的区间的信息可以被合并成两个区间的并区间的信息”。即问题是可以被分解解决的。



(3):区间或节点的更新 及 线段树的动态维护update (这是线段树核心价值所在,节点中的标记域可以解决N多种问题)

动态维护需要用到标记域,延迟标记等。

a:单节点更新

[cpp] view plaincopy
print?
  1. void Updata(int node, int begin, int end, int ind, int add)/*单节点更新*/    
  2. {    
  3.     
  4.     if( begin == end )    
  5.     {    
  6.         segTree[node] += add;    
  7.         return ;    
  8.     }    
  9.     int m = ( left + right ) >> 1;    
  10.     if(ind <= m)    
  11.         Updata(node * 2,left, m, ind, add);    
  12.     else    
  13.         Updata(node * 2 + 1, m + 1, right, ind, add);    
  14.     /*回溯更新父节点*/    
  15.     segTree[node] = min(segTree[node * 2], segTree[node * 2 + 1]);     
  16.          
  17. }   

b:区间更新(线段树中最有用的)

需要用到延迟标记,每个结点新增加一个标记,记录这个结点是否被进行了某种修改操作(这种修改操作会影响其子结点)。对于任意区间的修改,我们先按照查询的方式将其划分成线段树中的结点,然后修改这些结点的信息,并给这些结点标上代表这种修改操作的标记。在修改和查询的时候,如果我们到了一个结点p,并且决定考虑其子结点,那么我们就要看看结点p有没有标记,如果有,就要按照标记修改其子结点的信息,并且给子结点都标上相同的标记,同时消掉p的标记。(优点在于,不用将区间内的所有值都暴力更新,大大提高效率,因此区间更新是最优用的操作)

void Change来自dongxicheng.org

[cpp] view plaincopy
print?
  1. void Change(node *p, int a, int b) /* 当前考察结点为p,修改区间为(a,b]*/  
  2.    
  3. {  
  4.    
  5.   if (a <= p->Left && p->Right <= b)  
  6.    
  7.   /* 如果当前结点的区间包含在修改区间内*/  
  8.    
  9.   {  
  10.    
  11.      ...... /* 修改当前结点的信息,并标上标记*/  
  12.    
  13.      return;  
  14.    
  15.   }  
  16.    
  17.   Push_Down(p); /* 把当前结点的标记向下传递*/  
  18.    
  19.   int mid = (p->Left + p->Right) / 2; /* 计算左右子结点的分隔点 
  20.   
  21.   if (a < mid) Change(p->Lch, a, b); /* 和左孩子有交集,考察左子结点*/  
  22.    
  23.   if (b > mid) Change(p->Rch, a, b); /* 和右孩子有交集,考察右子结点*/  
  24.    
  25.   Update(p); /* 维护当前结点的信息(因为其子结点的信息可能有更改)*/  
  26.    
  27. }  



3:主要应用

(1):区间最值查询问题 (见模板1)

(2):连续区间修改或者单节点更新的动态查询问题 (见模板2)

(3):多维空间的动态查询 (见模板3)


二:典型模板

模板1:

RMQ,查询区间最值下标---min

[cpp] view plaincopy
print?
  1. #include<iostream>    
  2.   
  3. using namespace std;    
  4.     
  5. #define MAXN 100    
  6. #define MAXIND 256 //线段树节点个数    
  7.     
  8. //构建线段树,目的:得到M数组.    
  9. void build(int node, int b, int e, int M[], int A[])    
  10. {    
  11.     if (b == e)    
  12.         M[node] = b; //只有一个元素,只有一个下标    
  13.     else    
  14.     {     
  15.         build(2 * node, b, (b + e) / 2, M, A);    
  16.         build(2 * node + 1, (b + e) / 2 + 1, e, M, A);    
  17.   
  18.         if (A[M[2 * node]] <= A[M[2 * node + 1]])    
  19.             M[node] = M[2 * node];    
  20.         else    
  21.             M[node] = M[2 * node + 1];    
  22.     }    
  23. }    
  24.     
  25. //找出区间 [i, j] 上的最小值的索引    
  26. int query(int node, int b, int e, int M[], int A[], int i, int j)    
  27. {    
  28.     int p1, p2;    
  29.     
  30.     //查询区间和要求的区间没有交集    
  31.     if (i > e || j < b)    
  32.         return -1;    
  33.   
  34.     if (b >= i && e <= j)    
  35.         return M[node];    
  36.    
  37.     p1 = query(2 * node, b, (b + e) / 2, M, A, i, j);    
  38.     p2 = query(2 * node + 1, (b + e) / 2 + 1, e, M, A, i, j);    
  39.     
  40.     //return the position where the overall    
  41.     //minimum is    
  42.     if (p1 == -1)    
  43.         return M[node] = p2;    
  44.     if (p2 == -1)    
  45.         return M[node] = p1;    
  46.     if (A[p1] <= A[p2])    
  47.         return M[node] = p1;    
  48.     return M[node] = p2;    
  49.     
  50. }    
  51.     
  52.     
  53. int main()    
  54. {    
  55.     int M[MAXIND]; //下标1起才有意义,否则不是二叉树,保存下标编号节点对应区间最小值的下标.    
  56.     memset(M,-1,sizeof(M));    
  57.     int a[]={3,4,5,7,2,1,0,3,4,5};    
  58.     build(1, 0, sizeof(a)/sizeof(a[0])-1, M, a);    
  59.     cout<<query(1, 0, sizeof(a)/sizeof(a[0])-1, M, a, 0, 5)<<endl;    
  60.     return 0;    
  61. }    



模板2:

连续区间修改或者单节点更新的动态查询问题 (此模板查询区间和)

[cpp] view plaincopy
print?
  1. #include <cstdio>    
  2. #include <algorithm>    
  3. using namespace std;    
  4.      
  5. #define lson l , m , rt << 1    
  6. #define rson m + 1 , r , rt << 1 | 1   
  7. #define root 1 , N , 1   
  8. #define LL long long    
  9. const int maxn = 111111;    
  10. LL add[maxn<<2];    
  11. LL sum[maxn<<2];    
  12. void PushUp(int rt) {    
  13.     sum[rt] = sum[rt<<1] + sum[rt<<1|1];    
  14. }    
  15. void PushDown(int rt,int m) {    
  16.     if (add[rt]) {    
  17.         add[rt<<1] += add[rt];    
  18.         add[rt<<1|1] += add[rt];    
  19.         sum[rt<<1] += add[rt] * (m - (m >> 1));    
  20.         sum[rt<<1|1] += add[rt] * (m >> 1);    
  21.         add[rt] = 0;    
  22.     }    
  23. }    
  24. void build(int l,int r,int rt) {    
  25.     add[rt] = 0;    
  26.     if (l == r) {    
  27.         scanf("%lld",&sum[rt]);    
  28.         return ;    
  29.     }    
  30.     int m = (l + r) >> 1;    
  31.     build(lson);    
  32.     build(rson);    
  33.     PushUp(rt);    
  34. }    
  35. void update(int L,int R,int c,int l,int r,int rt) {    
  36.     if (L <= l && r <= R) {    
  37.         add[rt] += c;    
  38.         sum[rt] += (LL)c * (r - l + 1);    
  39.         return ;    
  40.     }    
  41.     PushDown(rt , r - l + 1);    
  42.     int m = (l + r) >> 1;    
  43.     if (L <= m) update(L , R , c , lson);    
  44.     if (m < R) update(L , R , c , rson);    
  45.     PushUp(rt);    
  46. }    
  47. LL query(int L,int R,int l,int r,int rt) {    
  48.     if (L <= l && r <= R) {    
  49.         return sum[rt];    
  50.     }    
  51.     PushDown(rt , r - l + 1);    
  52.     int m = (l + r) >> 1;    
  53.     LL ret = 0;    
  54.     if (L <= m) ret += query(L , R , lson);    
  55.     if (m < R) ret += query(L , R , rson);    
  56.     return ret;    
  57. }    
  58. int main() {    
  59.     int N , Q;    
  60.     scanf("%d%d",&N,&Q);    
  61.     build(root);    
  62.     while (Q --) {    
  63.         char op[2];    
  64.         int a , b , c;    
  65.         scanf("%s",op);    
  66.         if (op[0] == 'Q') {    
  67.             scanf("%d%d",&a,&b);    
  68.             printf("%lld\n",query(a , b ,root));    
  69.         } else {    
  70.             scanf("%d%d%d",&a,&b,&c);    
  71.             update(a , b , c , root);    
  72.         }    
  73.     }    
  74.     return 0;    
  75. }    


模板3:

多维空间的动态查询



三:练习题目

下面是hh线段树代码,典型练习哇~

在代码前先介绍一些我的线段树风格:

  • maxn是题目给的最大区间,而节点数要开4倍,确切的来说节点数要开大于maxn的最小2x的两倍
  • lson和rson分辨表示结点的左儿子和右儿子,由于每次传参数的时候都固定是这几个变量,所以可以用预定于比较方便的表示
  • 以前的写法是另外开两个个数组记录每个结点所表示的区间,其实这个区间不必保存,一边算一边传下去就行,只需要写函数的时候多两个参数,结合lson和rson的预定义可以很方便
  • PushUP(int rt)是把当前结点的信息更新到父结点
  • PushDown(int rt)是把当前结点的信息更新给儿子结点
  • rt表示当前子树的根(root),也就是当前所在的结点

整理这些题目后我觉得线段树的题目整体上可以分成以下四个部分:



单点更新:最最基础的线段树,只更新叶子节点,然后把信息用PushUP(int r)这个函数更新上来


  • hdu1166 敌兵布阵
  • 题意:O(-1)
  • 思路:O(-1)
    线段树功能:update:单点增减 query:区间求和

code:

[cpp] view plaincopy
print?
  1. #include<cstring>  
  2. #include<iostream>  
  3.   
  4. #define M 50005  
  5. #define lson l,m,rt<<1  
  6. #define rson m+1,r,rt<<1|1  
  7. /*left,right,root,middle*/  
  8.   
  9. int sum[M<<2];  
  10.   
  11. inline void PushPlus(int rt)  
  12. {  
  13.     sum[rt] = sum[rt<<1] + sum[rt<<1|1];  
  14. }  
  15.   
  16. void Build(int l, int r, int rt)  
  17. {  
  18.     if(l == r)  
  19.     {  
  20.         scanf("%d", &sum[rt]);  
  21.         return ;  
  22.     }  
  23.     int m = ( l + r )>>1;  
  24.   
  25.     Build(lson);  
  26.     Build(rson);  
  27.     PushPlus(rt);  
  28. }  
  29.   
  30. void Updata(int p, int add, int l, int r, int rt)  
  31. {  
  32.   
  33.     if( l == r )  
  34.     {  
  35.         sum[rt] += add;  
  36.         return ;  
  37.     }  
  38.     int m = ( l + r ) >> 1;  
  39.     if(p <= m)  
  40.         Updata(p, add, lson);  
  41.     else  
  42.         Updata(p, add, rson);  
  43.   
  44.     PushPlus(rt);  
  45. }  
  46.   
  47. int Query(int L,int R,int l,int r,int rt)  
  48. {  
  49.     if( L <= l && r <= R )  
  50.     {  
  51.         return sum[rt];  
  52.     }  
  53.     int m = ( l + r ) >> 1;  
  54.     int ans=0;  
  55.     if(L<=m )  
  56.         ans+=Query(L,R,lson);  
  57.     if(R>m)  
  58.         ans+=Query(L,R,rson);  
  59.   
  60.     return ans;  
  61. }  
  62. int main()  
  63. {     
  64.     int T, n, a, b;  
  65.     scanf("%d",&T);  
  66.     forint i = 1; i <= T; ++i )  
  67.     {  
  68.         printf("Case %d:\n",i);  
  69.         scanf("%d",&n);  
  70.         Build(1,n,1);  
  71.   
  72.         char op[10];  
  73.   
  74.         while( scanf("%s",op) &&op[0]!='E' )  
  75.         {  
  76.   
  77.             scanf("%d %d", &a, &b);  
  78.             if(op[0] == 'Q')  
  79.                 printf("%d\n",Query(a,b,1,n,1));  
  80.             else if(op[0] == 'S')  
  81.                 Updata(a,-b,1,n,1);  
  82.             else  
  83.                 Updata(a,b,1,n,1);  
  84.   
  85.         }  
  86.     }  
  87.     return 0;  
  88. }  

hdu1754 I Hate It
题意:O(-1)
思路:O(-1)
线段树功能:update:单点替换 query:区间最值

[cpp] view plaincopy
print?
  1. #include <cstdio>  
  2. #include <algorithm>  
  3. using namespace std;  
  4.    
  5. #define lson l , m , rt << 1  
  6. #define rson m + 1 , r , rt << 1 | 1  
  7. const int maxn = 222222;  
  8. int MAX[maxn<<2];  
  9. void PushUP(int rt) {  
  10.     MAX[rt] = max(MAX[rt<<1] , MAX[rt<<1|1]);  
  11. }  
  12. void build(int l,int r,int rt) {  
  13.     if (l == r) {  
  14.         scanf("%d",&MAX[rt]);  
  15.         return ;  
  16.     }  
  17.     int m = (l + r) >> 1;  
  18.     build(lson);  
  19.     build(rson);  
  20.     PushUP(rt);  
  21. }  
  22. void update(int p,int sc,int l,int r,int rt) {  
  23.     if (l == r) {  
  24.         MAX[rt] = sc;  
  25.         return ;  
  26.     }  
  27.     int m = (l + r) >> 1;  
  28.     if (p <= m) update(p , sc , lson);  
  29.     else update(p , sc , rson);  
  30.     PushUP(rt);  
  31. }  
  32. int query(int L,int R,int l,int r,int rt) {  
  33.     if (L <= l && r <= R) {  
  34.         return MAX[rt];  
  35.     }  
  36.     int m = (l + r) >> 1;  
  37.     int ret = 0;  
  38.     if (L <= m) ret = max(ret , query(L , R , lson));  
  39.     if (R > m) ret = max(ret , query(L , R , rson));  
  40.     return ret;  
  41. }  
  42. int main() {  
  43.     int n , m;  
  44.     while (~scanf("%d%d",&n,&m)) {  
  45.         build(1 , n , 1);  
  46.         while (m --) {  
  47.             char op[2];  
  48.             int a , b;  
  49.             scanf("%s%d%d",op,&a,&b);  
  50.             if (op[0] == 'Q') printf("%d\n",query(a , b , 1 , n , 1));  
  51.             else update(a , b , 1 , n , 1);  
  52.         }  
  53.     }  
  54.     return 0;  
  55. }  

hdu1394 Minimum Inversion Number
题意:求Inversion后的最小逆序数
思路:用O(nlogn)复杂度求出最初逆序数后,就可以用O(1)的复杂度分别递推出其他解
线段树功能:update:单点增减 query:区间求和

[cpp] view plaincopy
print?
  1. #include <cstdio>  
  2. #include <algorithm>  
  3. using namespace std;  
  4.    
  5. #define lson l , m , rt << 1  
  6. #define rson m + 1 , r , rt << 1 | 1  
  7. const int maxn = 5555;  
  8. int sum[maxn<<2];  
  9. void PushUP(int rt) {  
  10.     sum[rt] = sum[rt<<1] + sum[rt<<1|1];  
  11. }  
  12. void build(int l,int r,int rt) {  
  13.     sum[rt] = 0;  
  14.     if (l == r) return ;  
  15.     int m = (l + r) >> 1;  
  16.     build(lson);  
  17.     build(rson);  
  18. }  
  19. void update(int p,int l,int r,int rt) {  
  20.     if (l == r) {  
  21.         sum[rt] ++;  
  22.         return ;  
  23.     }  
  24.     int m = (l + r) >> 1;  
  25.     if (p <= m) update(p , lson);  
  26.     else update(p , rson);  
  27.     PushUP(rt);  
  28. }  
  29. int query(int L,int R,int l,int r,int rt) {  
  30.     if (L <= l && r <= R) {  
  31.         return sum[rt];  
  32.     }  
  33.     int m = (l + r) >> 1;  
  34.     int ret = 0;  
  35.     if (L <= m) ret += query(L , R , lson);  
  36.     if (R > m) ret += query(L , R , rson);  
  37.     return ret;  
  38. }  
  39. int x[maxn];  
  40. int main() {  
  41.     int n;  
  42.     while (~scanf("%d",&n)) {  
  43.         build(0 , n - 1 , 1);  
  44.         int sum = 0;  
  45.         for (int i = 0 ; i < n ; i ++) {  
  46.             scanf("%d",&x[i]);  
  47.             sum += query(x[i] , n - 1 , 0 , n - 1 , 1);  
  48.             update(x[i] , 0 , n - 1 , 1);  
  49.         }  
  50.         int ret = sum;  
  51.         for (int i = 0 ; i < n ; i ++) {  
  52.             sum += n - x[i] - x[i] - 1;  
  53.             ret = min(ret , sum);  
  54.         }  
  55.         printf("%d\n",ret);  
  56.     }  
  57.     return 0;  
  58. }  

hdu2795 Billboard
题意:h*w的木板,放进一些1*L的物品,求每次放空间能容纳且最上边的位子
思路:每次找到最大值的位子,然后减去L
线段树功能:query:区间求最大值的位子(直接把update的操作在query里做了)

[cpp] view plaincopy
print?
  1. #include <cstdio>  
  2. #include <algorithm>  
  3. using namespace std;  
  4.    
  5. #define lson l , m , rt << 1  
  6. #define rson m + 1 , r , rt << 1 | 1  
  7. const int maxn = 222222;  
  8. int h , w , n;  
  9. int MAX[maxn<<2];  
  10. void PushUP(int rt) {  
  11.     MAX[rt] = max(MAX[rt<<1] , MAX[rt<<1|1]);  
  12. }  
  13. void build(int l,int r,int rt) {  
  14.     MAX[rt] = w;  
  15.     if (l == r) return ;  
  16.     int m = (l + r) >> 1;  
  17.     build(lson);  
  18.     build(rson);  
  19. }  
  20. int query(int x,int l,int r,int rt) {  
  21.     if (l == r) {  
  22.         MAX[rt] -= x;  
  23.         return l;  
  24.     }  
  25.     int m = (l + r) >> 1;  
  26.     int ret = (MAX[rt<<1] >= x) ? query(x , lson) : query(x , rson);  
  27.     PushUP(rt);  
  28.     return ret;  
  29. }  
  30. int main() {  
  31.     while (~scanf("%d%d%d",&h,&w,&n)) {  
  32.         if (h > n) h = n;  
  33.         build(1 , h , 1);  
  34.         while (n --) {  
  35.             int x;  
  36.             scanf("%d",&x);  
  37.             if (MAX[1] < x) puts("-1");  
  38.             else printf("%d\n",query(x , 1 , h , 1));  
  39.         }  
  40.     }  
  41.     return 0;  
  42. }  

成段更新(通常这对初学者来说是一道坎),需要用到延迟标记(或者说懒惰标记),简单来说就是每次更新的时候不要更新到底,用延迟标记使得更新延迟到下次需要更新or询问到的时候

hdu1698 Just a Hook
题意:O(-1)
思路:O(-1)
线段树功能:update:成段替换 (由于只query一次总区间,所以可以直接输出1结点的信息)
[cpp] view plaincopy
print?
  1. #include <cstdio>  
  2. #include <algorithm>  
  3. using namespace std;  
  4.    
  5. #define lson l , m , rt << 1  
  6. #define rson m + 1 , r , rt << 1 | 1  
  7. const int maxn = 111111;  
  8. int h , w , n;  
  9. int col[maxn<<2];  
  10. int sum[maxn<<2];  
  11. void PushUp(int rt) {  
  12.     sum[rt] = sum[rt<<1] + sum[rt<<1|1];  
  13. }  
  14. void PushDown(int rt,int m) {  
  15.     if (col[rt]) {  
  16.         col[rt<<1] = col[rt<<1|1] = col[rt];  
  17.         sum[rt<<1] = (m - (m >> 1)) * col[rt];  
  18.         sum[rt<<1|1] = (m >> 1) * col[rt];  
  19.         col[rt] = 0;  
  20.     }  
  21. }  
  22. void build(int l,int r,int rt) {  
  23.     col[rt] = 0;  
  24.     sum[rt] = 1;  
  25.     if (l == r) return ;  
  26.     int m = (l + r) >> 1;  
  27.     build(lson);  
  28.     build(rson);  
  29.     PushUp(rt);  
  30. }  
  31. void update(int L,int R,int c,int l,int r,int rt) {  
  32.     if (L <= l && r <= R) {  
  33.         col[rt] = c;  
  34.         sum[rt] = c * (r - l + 1);  
  35.         return ;  
  36.     }  
  37.     PushDown(rt , r - l + 1);  
  38.     int m = (l + r) >> 1;  
  39.     if (L <= m) update(L , R , c , lson);  
  40.     if (R > m) update(L , R , c , rson);  
  41.     PushUp(rt);  
  42. }  
  43. int main() {  
  44.     int T , n , m;  
  45.     scanf("%d",&T);  
  46.     for (int cas = 1 ; cas <= T ; cas ++) {  
  47.         scanf("%d%d",&n,&m);  
  48.         build(1 , n , 1);  
  49.         while (m --) {  
  50.             int a , b , c;  
  51.             scanf("%d%d%d",&a,&b,&c);  
  52.             update(a , b , c , 1 , n , 1);  
  53.         }  
  54.         printf("Case %d: The total value of the hook is %d.\n",cas , sum[1]);  
  55.     }  
  56.     return 0;  
  57. }  

poj3468 A Simple Problem with Integers
题意:O(-1)
思路:O(-1)
线段树功能:update:成段增减 query:区间求和

[cpp] view plaincopy
print?
  1. #include <cstdio>  
  2. #include <algorithm>  
  3. using namespace std;  
  4.    
  5. #define lson l , m , rt << 1  
  6. #define rson m + 1 , r , rt << 1 | 1  
  7. #define LL long long  
  8. const int maxn = 111111;  
  9. LL add[maxn<<2];  
  10. LL sum[maxn<<2];  
  11. void PushUp(int rt) {  
  12.     sum[rt] = sum[rt<<1] + sum[rt<<1|1];  
  13. }  
  14. void PushDown(int rt,int m) {  
  15.     if (add[rt]) {  
  16.         add[rt<<1] += add[rt];  
  17.         add[rt<<1|1] += add[rt];  
  18.         sum[rt<<1] += add[rt] * (m - (m >> 1));  
  19.         sum[rt<<1|1] += add[rt] * (m >> 1);  
  20.         add[rt] = 0;  
  21.     }  
  22. }  
  23. void build(int l,int r,int rt) {  
  24.     add[rt] = 0;  
  25.     if (l == r) {  
  26.         scanf("%lld",&sum[rt]);  
  27.         return ;  
  28.     }  
  29.     int m = (l + r) >> 1;  
  30.     build(lson);  
  31.     build(rson);  
  32.     PushUp(rt);  
  33. }  
  34. void update(int L,int R,int c,int l,int r,int rt) {  
  35.     if (L <= l && r <= R) {  
  36.         add[rt] += c;  
  37.         sum[rt] += (LL)c * (r - l + 1);  
  38.         return ;  
  39.     }  
  40.     PushDown(rt , r - l + 1);  
  41.     int m = (l + r) >> 1;  
  42.     if (L <= m) update(L , R , c , lson);  
  43.     if (m < R) update(L , R , c , rson);  
  44.     PushUp(rt);  
  45. }  
  46. LL query(int L,int R,int l,int r,int rt) {  
  47.     if (L <= l && r <= R) {  
  48.         return sum[rt];  
  49.     }  
  50.     PushDown(rt , r - l + 1);  
  51.     int m = (l + r) >> 1;  
  52.     LL ret = 0;  
  53.     if (L <= m) ret += query(L , R , lson);  
  54.     if (m < R) ret += query(L , R , rson);  
  55.     return ret;  
  56. }  
  57. int main() {  
  58.     int N , Q;  
  59.     scanf("%d%d",&N,&Q);  
  60.     build(1 , N , 1);  
  61.     while (Q --) {  
  62.         char op[2];  
  63.         int a , b , c;  
  64.         scanf("%s",op);  
  65.         if (op[0] == 'Q') {  
  66.             scanf("%d%d",&a,&b);  
  67.             printf("%lld\n",query(a , b , 1 , N , 1));  
  68.         } else {  
  69.             scanf("%d%d%d",&a,&b,&c);  
  70.             update(a , b , c , 1 , N , 1);  
  71.         }  
  72.     }  
  73.     return 0;  
  74. }  

poj2528 Mayor’s posters
题意:在墙上贴海报,海报可以互相覆盖,问最后可以看见几张海报
思路:这题数据范围很大,直接搞超时+超内存,需要离散化:
离散化简单的来说就是只取我们需要的值来用,比如说区间[1000,2000],[1990,2012] 我们用不到[-∞,999][1001,1989][1991,1999][2001,2011][2013,+∞]这些值,所以我只需要1000,1990,2000,2012就够了,将其分别映射到0,1,2,3,在于复杂度就大大的降下来了
所以离散化要保存所有需要用到的值,排序后,分别映射到1~n,这样复杂度就会小很多很多
而这题的难点在于每个数字其实表示的是一个单位长度(并非一个点),这样普通的离散化会造成许多错误(包括我以前的代码,poj这题数据奇弱)
给出下面两个简单的例子应该能体现普通离散化的缺陷:
例子一:1-10 1-4 5-10
例子二:1-10 1-4 6-10
普通离散化后都变成了[1,4][1,2][3,4]
线段2覆盖了[1,2],线段3覆盖了[3,4],那么线段1是否被完全覆盖掉了呢?
例子一是完全被覆盖掉了,而例子二没有被覆盖

为了解决这种缺陷,我们可以在排序后的数组上加些处理,比如说[1,2,6,10]
如果相邻数字间距大于1的话,在其中加上任意一个数字,比如加成[1,2,3,6,7,10],然后再做线段树就好了.
线段树功能:update:成段替换 query:简单hash

[cpp] view plaincopy
print?
  1. #include <cstdio>  
  2. #include <cstring>  
  3. #include <algorithm>  
  4. using namespace std;  
  5. #define lson l , m , rt << 1  
  6. #define rson m + 1 , r , rt << 1 | 1  
  7.    
  8. const int maxn = 11111;  
  9. bool hash[maxn];  
  10. int li[maxn] , ri[maxn];  
  11. int X[maxn*3];  
  12. int col[maxn<<4];  
  13. int cnt;  
  14.    
  15. void PushDown(int rt) {  
  16.     if (col[rt] != -1) {  
  17.         col[rt<<1] = col[rt<<1|1] = col[rt];  
  18.         col[rt] = -1;  
  19.     }  
  20. }  
  21. void update(int L,int R,int c,int l,int r,int rt) {  
  22.     if (L <= l && r <= R) {  
  23.         col[rt] = c;  
  24.         return ;  
  25.     }  
  26.     PushDown(rt);  
  27.     int m = (l + r) >> 1;  
  28.     if (L <= m) update(L , R , c , lson);  
  29.     if (m < R) update(L , R , c , rson);  
  30. }  
  31. void query(int l,int r,int rt) {  
  32.     if (col[rt] != -1) {  
  33.         if (!hash[col[rt]]) cnt ++;  
  34.         hash[ col[rt] ] = true;  
  35.         return ;  
  36.     }  
  37.     if (l == r) return ;  
  38.     int m = (l + r) >> 1;  
  39.     query(lson);  
  40.     query(rson);  
  41. }  
  42. int Bin(int key,int n,int X[]) {  
  43.     int l = 0 , r = n - 1;  
  44.     while (l <= r) {  
  45.         int m = (l + r) >> 1;  
  46.         if (X[m] == key) return m;  
  47.         if (X[m] < key) l = m + 1;  
  48.         else r = m - 1;  
  49.     }  
  50.     return -1;  
  51. }  
  52. int main() {  
  53.     int T , n;  
  54.     scanf("%d",&T);  
  55.     while (T --) {  
  56.         scanf("%d",&n);  
  57.         int nn = 0;  
  58.         for (int i = 0 ; i < n ; i ++) {  
  59.             scanf("%d%d",&li[i] , &ri[i]);  
  60.             X[nn++] = li[i];  
  61.             X[nn++] = ri[i];  
  62.         }  
  63.         sort(X , X + nn);  
  64.         int m = 1;  
  65.         for (int i = 1 ; i < nn; i ++) {  
  66.             if (X[i] != X[i-1]) X[m ++] = X[i];  
  67.         }  
  68.         for (int i = m - 1 ; i > 0 ; i --) {  
  69.             if (X[i] != X[i-1] + 1) X[m ++] = X[i-1] + 1;  
  70.         }  
  71.         sort(X , X + m);  
  72.         memset(col , -1 , sizeof(col));  
  73.         for (int i = 0 ; i < n ; i ++) {  
  74.             int l = Bin(li[i] , m , X);  
  75.             int r = Bin(ri[i] , m , X);  
  76.             update(l , r , i , 0 , m , 1);  
  77.         }  
  78.         cnt = 0;  
  79.         memset(hash , false , sizeof(hash));  
  80.         query(0 , m , 1);  
  81.         printf("%d\n",cnt);  
  82.     }  
  83.     return 0;  
  84. }  

poj3225 Help with Intervals
题意:区间操作,交,并,补等
思路:
我们一个一个操作来分析:(用0和1表示是否包含区间,-1表示该区间内既有包含又有不包含)
U:把区间[l,r]覆盖成1
I:把[-∞,l)(r,∞]覆盖成0
D:把区间[l,r]覆盖成0
C:把[-∞,l)(r,∞]覆盖成0 , 且[l,r]区间0/1互换
S:[l,r]区间0/1互换

成段覆盖的操作很简单,比较特殊的就是区间0/1互换这个操作,我们可以称之为异或操作
很明显我们可以知道这个性质:当一个区间被覆盖后,不管之前有没有异或标记都没有意义了
所以当一个节点得到覆盖标记时把异或标记清空
而当一个节点得到异或标记的时候,先判断覆盖标记,如果是0或1,直接改变一下覆盖标记,不然的话改变异或标记

开区间闭区间只要数字乘以2就可以处理(偶数表示端点,奇数表示两端点间的区间)
线段树功能:update:成段替换,区间异或 query:简单hash

[cpp] view plaincopy
print?
  1. #include <cstdio>  
  2. #include <cstring>  
  3. #include <cctype>  
  4. #include <algorithm>  
  5. using namespace std;  
  6. #define lson l , m , rt << 1  
  7. #define rson m + 1 , r , rt << 1 | 1  
  8.    
  9. const int maxn = 131072;  
  10. bool hash[maxn+1];  
  11. int cover[maxn<<2];  
  12. int XOR[maxn<<2];  
  13. void FXOR(int rt) {  
  14.     if (cover[rt] != -1) cover[rt] ^= 1;  
  15.     else XOR[rt] ^= 1;  
  16. }  
  17. void PushDown(int rt) {  
  18.     if (cover[rt] != -1) {  
  19.         cover[rt<<1] = cover[rt<<1|1] = cover[rt];  
  20.         XOR[rt<<1] = XOR[rt<<1|1] = 0;  
  21.         cover[rt] = -1;  
  22.     }  
  23.     if (XOR[rt]) {  
  24.         FXOR(rt<<1);  
  25.         FXOR(rt<<1|1);  
  26.         XOR[rt] = 0;  
  27.     }  
  28. }  
  29. void update(char op,int L,int R,int l,int r,int rt) {  
  30.     if (L <= l && r <= R) {  
  31.         if (op == 'U') {  
  32.             cover[rt] = 1;  
  33.             XOR[rt] = 0;  
  34.         } else if (op == 'D') {  
  35.             cover[rt] = 0;  
  36.             XOR[rt] = 0;  
  37.         } else if (op == 'C' || op == 'S') {  
  38.             FXOR(rt);  
  39.         }  
  40.         return ;  
  41.     }  
  42.     PushDown(rt);  
  43.     int m = (l + r) >> 1;  
  44.     if (L <= m) update(op , L , R , lson);  
  45.     else if (op == 'I' || op == 'C') {  
  46.         XOR[rt<<1] = cover[rt<<1] = 0;  
  47.     }  
  48.     if (m < R) update(op , L , R , rson);  
  49.     else if (op == 'I' || op == 'C') {  
  50.         XOR[rt<<1|1] = cover[rt<<1|1] = 0;  
  51.     }  
  52. }  
  53. void query(int l,int r,int rt) {  
  54.     if (cover[rt] == 1) {  
  55.         for (int it = l ; it <= r ; it ++) {  
  56.             hash[it] = true;  
  57.         }  
  58.         return ;  
  59.     } else if (cover[rt] == 0) return ;  
  60.     if (l == r) return ;  
  61.     PushDown(rt);  
  62.     int m = (l + r) >> 1;  
  63.     query(lson);  
  64.     query(rson);  
  65. }  
  66. int main() {  
  67.     cover[1] = XOR[1] = 0;  
  68.     char op , l , r;  
  69.     int a , b;  
  70.     while ( ~scanf("%c %c%d,%d%c\n",&op , &l , &a , &b , &r) ) {  
  71.         a <<= 1 , b <<= 1;  
  72.         if (l == '(') a ++;  
  73.         if (r == ')') b --;  
  74.         if (a > b) {  
  75.             if (op == 'C' || op == 'I') {  
  76.                 cover[1] = XOR[1] = 0;  
  77.             }  
  78.         } else update(op , a , b , 0 , maxn , 1);  
  79.     }  
  80.     query(0 , maxn , 1);  
  81.     bool flag = false;  
  82.     int s = -1 , e;  
  83.     for (int i = 0 ; i <= maxn ; i ++) {  
  84.         if (hash[i]) {  
  85.             if (s == -1) s = i;  
  86.             e = i;  
  87.         } else {  
  88.             if (s != -1) {  
  89.                 if (flag) printf(" ");  
  90.                 flag = true;  
  91.                 printf("%c%d,%d%c",s&1?'(':'[' , s>>1 , (e+1)>>1 , e&1?')':']');  
  92.                 s = -1;  
  93.             }  
  94.         }  
  95.     }  
  96.     if (!flag) printf("empty set");  
  97.     puts("");  
  98.     return 0;  
  99. }  
练习
poj1436 Horizontally Visible Segments
poj2991 Crane
Another LCIS
Bracket Sequence

区间合并

这类题目会询问区间中满足条件的连续最长区间,所以PushUp的时候需要对左右儿子的区间进行合并
poj3667 Hotel
题意:1 a:询问是不是有连续长度为a的空房间,有的话住进最左边
2 a b:将[a,a+b-1]的房间清空
思路:记录区间中最长的空房间
线段树操作:update:区间替换 query:询问满足条件的最左断点
[cpp] view plaincopy
print?
  1. #include <cstdio>  
  2. #include <cstring>  
  3. #include <cctype>  
  4. #include <algorithm>  
  5. using namespace std;  
  6. #define lson l , m , rt << 1  
  7. #define rson m + 1 , r , rt << 1 | 1  
  8.    
  9. const int maxn = 55555;  
  10. int lsum[maxn<<2] , rsum[maxn<<2] , msum[maxn<<2];  
  11. int cover[maxn<<2];  
  12.    
  13. void PushDown(int rt,int m) {  
  14.     if (cover[rt] != -1) {  
  15.         cover[rt<<1] = cover[rt<<1|1] = cover[rt];  
  16.         msum[rt<<1] = lsum[rt<<1] = rsum[rt<<1] = cover[rt] ? 0 : m - (m >> 1);  
  17.         msum[rt<<1|1] = lsum[rt<<1|1] = rsum[rt<<1|1] = cover[rt] ? 0 : (m >> 1);  
  18.         cover[rt] = -1;  
  19.     }  
  20. }  
  21. void PushUp(int rt,int m) {  
  22.     lsum[rt] = lsum[rt<<1];  
  23.     rsum[rt] = rsum[rt<<1|1];  
  24.     if (lsum[rt] == m - (m >> 1)) lsum[rt] += lsum[rt<<1|1];  
  25.     if (rsum[rt] == (m >> 1)) rsum[rt] += rsum[rt<<1];  
  26.     msum[rt] = max(lsum[rt<<1|1] + rsum[rt<<1] , max(msum[rt<<1] , msum[rt<<1|1]));  
  27. }  
  28. void build(int l,int r,int rt) {  
  29.     msum[rt] = lsum[rt] = rsum[rt] = r - l + 1;  
  30.     cover[rt] = -1;  
  31.     if (l == r) return ;  
  32.     int m = (l + r) >> 1;  
  33.     build(lson);  
  34.     build(rson);  
  35. }  
  36. void update(int L,int R,int c,int l,int r,int rt) {  
  37.     if (L <= l && r <= R) {  
  38.         msum[rt] = lsum[rt] = rsum[rt] = c ? 0 : r - l + 1;  
  39.         cover[rt] = c;  
  40.         return ;  
  41.     }  
  42.     PushDown(rt , r - l + 1);  
  43.     int m = (l + r) >> 1;  
  44.     if (L <= m) update(L , R , c , lson);  
  45.     if (m < R) update(L , R , c , rson);  
  46.     PushUp(rt , r - l + 1);  
  47. }  
  48. int query(int w,int l,int r,int rt) {  
  49.     if (l == r) return l;  
  50.     PushDown(rt , r - l + 1);  
  51.     int m = (l + r) >> 1;  
  52.     if (msum[rt<<1] >= w) return query(w , lson);  
  53.     else if (rsum[rt<<1] + lsum[rt<<1|1] >= w) return m - rsum[rt<<1] + 1;  
  54.     return query(w , rson);  
  55. }  
  56. int main() {  
  57.     int n , m;  
  58.     scanf("%d%d",&n,&m);  
  59.     build(1 , n , 1);  
  60.     while (m --) {  
  61.         int op , a , b;  
  62.         scanf("%d",&op);  
  63.         if (op == 1) {  
  64.             scanf("%d",&a);  
  65.             if (msum[1] < a) puts("0");  
  66.             else {  
  67.                 int p = query(a , 1 , n , 1);  
  68.                 printf("%d\n",p);  
  69.                 update(p , p + a - 1 , 1 , 1 , n , 1);  
  70.             }  
  71.         } else {  
  72.             scanf("%d%d",&a,&b);  
  73.             update(a , a + b - 1 , 0 , 1 , n , 1);  
  74.         }  
  75.     }  
  76.     return 0;  
  77. }  

练习
hdu3308 LCIS
hdu3397 Sequence operation
hdu2871 Memory Control
hdu1540 Tunnel Warfare
CF46-D Parking Lot

扫描线

这类题目需要将一些操作排序,然后从左到右用一根扫描线(当然是在我们脑子里)扫过去
最典型的就是矩形面积并,周长并等题

hdu1542 Atlantis
题意:矩形面积并
思路:浮点数先要离散化;然后把矩形分成两条边,上边和下边,对横轴建树,然后从下到上扫描上去,用cnt表示该区间下边比上边多几个,sum代表该区间内被覆盖的线段的长度总和
这里线段树的一个结点并非是线段的一个端点,而是该端点和下一个端点间的线段,所以题目中r+1,r-1的地方可以自己好好的琢磨一下
线段树操作:update:区间增减 query:直接取根节点的值
[cpp] view plaincopy
print?
  1. #include <cstdio>  
  2. #include <cstring>  
  3. #include <cctype>  
  4. #include <algorithm>  
  5. using namespace std;  
  6. #define lson l , m , rt << 1  
  7. #define rson m + 1 , r , rt << 1 | 1  
  8.    
  9. const int maxn = 2222;  
  10. int cnt[maxn << 2];  
  11. double sum[maxn << 2];  
  12. double X[maxn];  
  13. struct Seg {  
  14.     double h , l , r;  
  15.     int s;  
  16.     Seg(){}  
  17.     Seg(double a,double b,double c,int d) : l(a) , r(b) , h(c) , s(d) {}  
  18.     bool operator < (const Seg &cmp) const {  
  19.         return h < cmp.h;  
  20.     }  
  21. }ss[maxn];  
  22. void PushUp(int rt,int l,int r) {  
  23.     if (cnt[rt]) sum[rt] = X[r+1] - X[l];  
  24.     else if (l == r) sum[rt] = 0;  
  25.     else sum[rt] = sum[rt<<1] + sum[rt<<1|1];  
  26. }  
  27. void update(int L,int R,int c,int l,int r,int rt) {  
  28.     if (L <= l && r <= R) {  
  29.         cnt[rt] += c;  
  30.         PushUp(rt , l , r);  
  31.         return ;  
  32.     }  
  33.     int m = (l + r) >> 1;  
  34.     if (L <= m) update(L , R , c , lson);  
  35.     if (m < R) update(L , R , c , rson);  
  36.     PushUp(rt , l , r);  
  37. }  
  38. int Bin(double key,int n,double X[]) {  
  39.     int l = 0 , r = n - 1;  
  40.     while (l <= r) {  
  41.         int m = (l + r) >> 1;  
  42.         if (X[m] == key) return m;  
  43.         if (X[m] < key) l = m + 1;  
  44.         else r = m - 1;  
  45.     }  
  46.     return -1;  
  47. }  
  48. int main() {  
  49.     int n , cas = 1;  
  50.     while (~scanf("%d",&n) && n) {  
  51.         int m = 0;  
  52.         while (n --) {  
  53.             double a , b , c , d;  
  54.             scanf("%lf%lf%lf%lf",&a,&b,&c,&d);  
  55.             X[m] = a;  
  56.             ss[m++] = Seg(a , c , b , 1);  
  57.             X[m] = c;  
  58.             ss[m++] = Seg(a , c , d , -1);  
  59.         }  
  60.         sort(X , X + m);  
  61.         sort(ss , ss + m);  
  62.         int k = 1;  
  63.         for (int i = 1 ; i < m ; i ++) {  
  64.             if (X[i] != X[i-1]) X[k++] = X[i];  
  65.         }  
  66.         memset(cnt , 0 , sizeof(cnt));  
  67.         memset(sum , 0 , sizeof(sum));  
  68.         double ret = 0;  
  69.         for (int i = 0 ; i < m - 1 ; i ++) {  
  70.             int l = Bin(ss[i].l , k , X);  
  71.             int r = Bin(ss[i].r , k , X) - 1;  
  72.             if (l <= r) update(l , r , ss[i].s , 0 , k - 1, 1);  
  73.             ret += sum[1] * (ss[i+1].h - ss[i].h);  
  74.         }  
  75.         printf("Test case #%d\nTotal explored area: %.2lf\n\n",cas++ , ret);  
  76.     }  
  77.     return 0;  
  78. }  

hdu1828 Picture
题意:矩形周长并
思路:与面积不同的地方是还要记录竖的边有几个(numseg记录),并且当边界重合的时候需要合并(用lbd和rbd表示边界来辅助)
线段树操作:update:区间增减 query:直接取根节点的值

[cpp] view plaincopy
print?
  1. #include <cstdio>  
  2. #include <cstring>  
  3. #include <cctype>  
  4. #include <algorithm>  
  5. using namespace std;  
  6. #define lson l , m , rt << 1  
  7. #define rson m + 1 , r , rt << 1 | 1  
  8.    
  9. const int maxn = 22222;  
  10. struct Seg{  
  11.     int l , r , h , s;  
  12.     Seg() {}  
  13.     Seg(int a,int b,int c,int d):l(a) , r(b) , h(c) , s(d) {}  
  14.     bool operator < (const Seg &cmp) const {  
  15.         if (h == cmp.h) return s > cmp.s;  
  16.         return h < cmp.h;  
  17.     }  
  18. }ss[maxn];  
  19. bool lbd[maxn<<2] , rbd[maxn<<2];  
  20. int numseg[maxn<<2];  
  21. int cnt[maxn<<2];  
  22. int len[maxn<<2];  
  23. void PushUP(int rt,int l,int r) {  
  24.     if (cnt[rt]) {  
  25.         lbd[rt] = rbd[rt] = 1;  
  26.         len[rt] = r - l + 1;  
  27.         numseg[rt] = 2;  
  28.     } else if (l == r) {  
  29.         len[rt] = numseg[rt] = lbd[rt] = rbd[rt] = 0;  
  30.     } else {  
  31.         lbd[rt] = lbd[rt<<1];  
  32.         rbd[rt] = rbd[rt<<1|1];  
  33.         len[rt] = len[rt<<1] + len[rt<<1|1];  
  34.         numseg[rt] = numseg[rt<<1] + numseg[rt<<1|1];  
  35.         if (lbd[rt<<1|1] && rbd[rt<<1]) numseg[rt] -= 2;//两条线重合  
  36.     }  
  37. }  
  38. void update(int L,int R,int c,int l,int r,int rt) {  
  39.     if (L <= l && r <= R) {  
  40.         cnt[rt] += c;  
  41.         PushUP(rt , l , r);  
  42.         return ;  
  43.     }  
  44.     int m = (l + r) >> 1;  
  45.     if (L <= m) update(L , R , c , lson);  
  46.     if (m < R) update(L , R , c , rson);  
  47.     PushUP(rt , l , r);  
  48. }  
  49. int main() {  
  50.     int n;  
  51.     while (~scanf("%d",&n)) {  
  52.         int m = 0;  
  53.         int lbd = 10000, rbd = -10000;  
  54.         for (int i = 0 ; i < n ; i ++) {  
  55.             int a , b , c , d;  
  56.             scanf("%d%d%d%d",&a,&b,&c,&d);  
  57.             lbd = min(lbd , a);  
  58.             rbd = max(rbd , c);  
  59.             ss[m++] = Seg(a , c , b , 1);  
  60.             ss[m++] = Seg(a , c , d , -1);  
  61.         }  
  62.         sort(ss , ss + m);  
  63.         int ret = 0 , last = 0;  
  64.         for (int i = 0 ; i < m ; i ++) {  
  65.             if (ss[i].l < ss[i].r) update(ss[i].l , ss[i].r - 1 , ss[i].s , lbd , rbd - 1 , 1);  
  66.             ret += numseg[1] * (ss[i+1].h - ss[i].h);  
  67.             ret += abs(len[1] - last);  
  68.             last = len[1];  
  69.         }  
  70.         printf("%d\n",ret);  
  71.     }  
  72.     return 0;  
  73. }  

练习
hdu3265 Posters
hdu3642 Get The Treasury
poj2482 Stars in Your Window
poj2464 Brownie Points II
hdu3255 Farming 
ural1707 Hypnotoad’s Secret
uva11983 Weird Advertisement

多颗线段树问题

此类题目主用特点是区间不连续,有一定规律间隔,用多棵树表示不同的偏移区间
hdu 4288 coder
题意:
维护一个有序数列{An},有三种操作:
1、添加一个元素。
2、删除一个元素。
3、求数列中下标%5 = 3的值的和。

由于有删除和添加操作,所以离线离散操作,节点中cnt存储区间中有几个数,sum存储偏移和
[cpp] view plaincopy
print?
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<cstring>  
  4. #include<algorithm>  
  5. using namespace std;  
  6. const int maxn=100002;  
  7.   
  8. #define lson l , m , rt << 1    
  9. #define rson m + 1 , r , rt << 1 | 1   
  10.   
  11. __int64 sum[maxn<<2][6];  
  12. int cnt[maxn << 2];  
  13.   
  14. char op[maxn][20];  
  15. int a[maxn];  
  16.   
  17. int X[maxn];  
  18.   
  19. void PushUp(int rt)  
  20. {  
  21.     cnt[rt] = cnt[rt<<1] + cnt[rt<<1|1];  
  22.       
  23.     int offset = cnt[rt<<1];  
  24.     for(int i = 0; i < 5; ++i)  
  25.     {  
  26.         sum[rt][i] = sum[rt<<1][i];  
  27.     }  
  28.     for(int i = 0; i < 5; ++i)  
  29.     {  
  30.         sum[rt][(i + offset) % 5] += sum[rt<<1|1][i];  
  31.     }  
  32. }  
  33.   
  34. void Build(int l, int r, int rt)    
  35. {   /*此题Build完全可以用一个memset代替*/  
  36.     cnt[rt] = 0;  
  37.     for(int i = 0; i < 5; ++i)   sum[rt][i] = 0;  
  38.     if( l == r ) return;  
  39.     int m = ( l + r )>>1;      
  40.     Build(lson);    
  41.     Build(rson);     
  42. }   
  43.   
  44. void Updata(int p, int op, int l, int r, int rt)    
  45. {     
  46.     if( l == r )    
  47.     {    
  48.         cnt[rt] = op;   
  49.         sum[rt][1] = op * X[l-1];   
  50.         return ;    
  51.     }    
  52.     int m = ( l + r ) >> 1;    
  53.     if(p <= m)    
  54.         Updata(p, op, lson);    
  55.     else    
  56.         Updata(p, op, rson);    
  57.     
  58.     PushUp(rt);    
  59. }   
  60.   
  61. int main()  
  62. {  
  63.     int n;  
  64.     while(scanf("%d", &n) != EOF)  
  65.     {  
  66.         int nn = 0;  
  67.         for(int i = 0; i < n; ++i)  
  68.         {  
  69.             scanf("%s", &op[i]);  
  70.               
  71.             if(op[i][0] != 's')  
  72.             {  
  73.                 scanf("%d", &a[i]);  
  74.                 if(op[i][0] == 'a')  
  75.                 {  
  76.                     X[nn++] = a[i];  
  77.                 }  
  78.             }  
  79.         }  
  80.           
  81.         sort(X,X+nn);/*unique前必须sort*/  
  82.         nn = unique(X, X + nn) - X; /*去重并得到总数*/  
  83.           
  84.         Build(1, nn, 1);  
  85.           
  86.         for(int i = 0; i < n; ++i)  
  87.         {  
  88.             int pos = upper_bound(X, X+nn, a[i]) - X; /* hash */   
  89.             if(op[i][0] == 'a')  
  90.             {  
  91.                 Updata(pos, 1, 1, nn, 1);  
  92.             }  
  93.             else if(op[i][0] == 'd')  
  94.             {  
  95.                 Updata(pos, 0, 1, nn, 1);  
  96.             }  
  97.             else printf("%I64d\n",sum[1][3]);  
  98.         }  
  99.     }  
  100.     return 0;  
  101. }  

2:hdu 4267 A simple problem with integers
题目:给出n个数,每次将一段区间内满足(i-l)%k==0  (r>=i>=l) 的数ai增加c, 最后单点查询。
这种题目更新的区间是零散的,如果可以通过某种方式让离散的都变得连续,那么问题就可以用线段树完美解决。解决方式一般也是固定的,那就是利用题意维护多颗线段树。此题虚维护55颗,更新最终确定在一颗上,查询则将查询点被包含的树全部叠加。
[cpp] view plaincopy
print?
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<cstring>  
  4. #include<cmath>  
  5. #include<algorithm>  
  6. #include<set>  
  7. #include<vector>  
  8. #include<string>  
  9. #include<map>  
  10. #define eps 1e-7  
  11. #define LL long long  
  12. #define N 500005  
  13. #define zero(a) fabs(a)<eps  
  14. #define lson step<<1  
  15. #define rson step<<1|1  
  16. #define MOD 1234567891  
  17. #define pb(a) push_back(a)  
  18. using namespace std;  
  19. struct Node{  
  20.     int left,right,add[55],sum;  
  21.     int mid(){return (left+right)/2;}  
  22. }L[4*N];  
  23. int a[N],n,b[11][11];  
  24. void Bulid(int step ,int l,int r){  
  25.     L[step].left=l;  
  26.     L[step].right=r;  
  27.     L[step].sum=0;  
  28.     memset(L[step].add,0,sizeof(L[step].add));  
  29.     if(l==r) return ;  
  30.     Bulid(lson,l,L[step].mid());  
  31.     Bulid(rson,L[step].mid()+1,r);  
  32. }  
  33. void push_down(int step){  
  34.     if(L[step].sum){  
  35.         L[lson].sum+=L[step].sum;  
  36.         L[rson].sum+=L[step].sum;  
  37.         L[step].sum=0;  
  38.         for(int i=0;i<55;i++){  
  39.                 L[lson].add[i]+=L[step].add[i];  
  40.                 L[rson].add[i]+=L[step].add[i];  
  41.                 L[step].add[i]=0;  
  42.         }  
  43.     }  
  44. }  
  45. void update(int step,int l,int r,int num,int i,int j){  
  46.     if(L[step].left==l&&L[step].right==r){  
  47.         L[step].sum+=num;  
  48.         L[step].add[b[i][j]]+=num;  
  49.         return;  
  50.     }  
  51.     push_down(step);  
  52.     if(r<=L[step].mid()) update(lson,l,r,num,i,j);  
  53.     else if(l>L[step].mid()) update(rson,l,r,num,i,j);  
  54.     else {  
  55.         update(lson,l,L[step].mid(),num,i,j);  
  56.         update(rson,L[step].mid()+1,r,num,i,j);  
  57.     }  
  58. }  
  59. int query(int step,int pos){  
  60.     if(L[step].left==L[step].right){  
  61.         int tmp=0;  
  62.         for(int i=1;i<=10;i++)  tmp+=L[step].add[b[i][pos%i]];  
  63.         return a[L[step].left]+tmp;  
  64.     }  
  65.     push_down(step);  
  66.     if(pos<=L[step].mid()) return query(lson,pos);  
  67.     else return query(rson,pos);  
  68. }  
  69. int main(){  
  70.     int cnt=0;  
  71.     for(int i=1;i<=10;i++) for(int j=0;j<i;j++) b[i][j]=cnt++;  
  72.     while(scanf("%d",&n)!=EOF){  
  73.         for(int i=1;i<=n;i++) scanf("%d",&a[i]);  
  74.         Bulid(1,1,n);  
  75.         int q,d;  
  76.         scanf("%d",&q);  
  77.         while(q--){  
  78.             int k,l,r,m;  
  79.             scanf("%d",&k);  
  80.             if(k==2){  
  81.                 scanf("%d",&m);  
  82.                 printf("%d\n",query(1,m));  
  83.             }  
  84.             else{  
  85.                 scanf("%d%d%d%d",&l,&r,&d,&m);  
  86.                 update(1,l,r,m,d,l%d);  
  87.             }  
  88.         }  
  89.     }  
  90.     return 0;  
  91. }  




线段树与其他结合练习(欢迎大家补充):

  • hdu3954 Level up
  • hdu4027 Can you answer these queries?
  • hdu3333 Turing Tree
  • hdu3874 Necklace
  • hdu3016 Man Down
  • hdu3340 Rain in ACStar
  • zju3511 Cake Robbery
  • UESTC1558 Charitable Exchange
  • CF85-D Sum of Medians
  • spojGSS2 Can you answer these queries II


51
1

我的同类文章

  • 天马行空的ACM现场赛回顾2013-11-13
  • hdu 4711 Weather概率dp2013-09-20
  • hdu 4722 GoodNumbers2013-09-19
  • 动态规划-各种整数划分2013-09-01
  • Three Swaps DFS2013-08-31
  • 单点更新线段树 RMQ2013-08-27
  • Simple Template2013-10-20
  • 数据结构---各种树模板 持续更新···2013-09-20
  • hdu 4712 Hamming distance2013-09-19
  • Book of Evil 树双向DFS2013-08-31
  • 双向DFS模板题2013-08-30
更多文章

参考知识库

img

.NET知识库

猜你在找
顾荣:开源大数据存储系统Alluxio(原Tachyon)的原理分析与案例简介
数据结构(C版)
Ceph—分布式存储系统的另一个选择
Docker与容器服务扩展机制 - 存储
数据结构基础系列(5):数组与广义表
cdoj 2015数据结构专题M - 秋实大哥与线段树
CUGB专题训练之数据结构B - Count Color 线段树区间更新
数据结构线段树单点更新
数据结构线段树详解超详细
活用各种数据结构线段树篇
查看评论
26楼 消磨时间 2016-08-10 16:19发表 [回复]
请问,为什么要用四倍的空间。谢谢你。
25楼 bighero4 2016-06-26 21:26发表 [回复]
看明白了,那个build的例子,每个区间节点的value代表此区间(在数组array中是下标范围)上的的数组array的最小值。
24楼 bighero4 2016-06-26 21:15发表 [回复]
把所有错误纠正一下吧。比如说:线段树并非完全二叉树。
有些地方做一下解释,比如Build中的Value是什么意思?
23楼 Sci_M3 2016-06-08 11:42发表 [回复]
模板一是有错的,顺便这个错就是您自己跑的样例的问题...
22楼 zlqdhrdhrdhr 2015-11-29 09:27发表 [回复]
博主第3块代码有个小错误
那个left跟right应该是begin和end吧 ^_^
Re: wyj__jenny 2016-02-04 11:39发表 [回复]
回复zlqdhrdhrdhr:同意
21楼 清风小竹 2015-11-03 16:12发表 [回复]
楼主的博客写的很好!赞一个
20楼 liangshanxiaohan 2015-10-28 21:01发表 [回复]
楼主你好,我想请问一下,你是否接触过range tree,能不能给一段range tree建树的代码
19楼 Lormons 2015-09-27 11:43发表 [回复]
不是完全二叉树吧...~~
18楼 LuckyqXd 2015-08-22 23:22发表 [回复]
线段树可以不为完全二叉树。。。。
17楼 bestcoder_judge 2015-08-21 15:44发表 [回复]
不错不错,线段树估计你没问题了!
16楼 Triangle_libing 2015-08-12 10:28发表 [回复]
这跟编译器有什么关系
15楼 Anger_Coder 2015-07-03 18:46发表 [回复]
有一个点需要注意,mid的求法,不要(begin+end)/2这个会溢出,要begin + (end - begin) >> 1 这样比较好
Re: glassMonster 2015-11-17 19:15发表 [回复]
回复Anger_Coder:不过需要注意一点,移位运算符的优先级低,在这里就比“+”低,最后的结果就是end>>1,会使程序运行出错,所以最好再加括号begin+((end-begin)>>1)
14楼 小飞哇咔咔 2015-04-01 12:01发表 [回复]
mark
13楼 ouweiqi 2015-03-30 11:13发表 [回复]
错误百出,还好意思贴出来
Re: Echizens 2015-08-20 15:16发表 [回复]
回复ouweiqi:至少人家很谦虚。所以有意见提出来,不要BB。
12楼 楼上小宇 2015-01-27 23:43发表 [回复]
受教了。。。很详细
11楼 MetalSeed 2014-11-12 16:09发表 [回复]
228003430
10楼 累了泪了funny 2014-07-25 20:53发表 [回复]
为什么都不喜欢定义结构体呢,表示用数组模拟,看不懂,不要嘲笑我,我是菜鸟
Re: MetalSeed 2014-11-07 09:46发表 [回复]
回复u011422701:那个时候强迫症,数组写起来方便一些,速度稍微快一点
9楼 MetalSeed 2013-01-01 23:50发表 [回复]
http://blog.csdn.net/shiqi_614/article/details/8228102
8楼 GodHunter47 2012-12-31 15:16发表 [回复]
弱弱的屌丝一枚,不过勉为其难的给你顶一下
Re: MetalSeed 2013-01-01 23:47发表 [回复]
回复GodHunter47:```` ~~ 呼呼 谢谢大神
7楼 Zr777111 2012-10-12 15:11发表 [回复]
高深。。。
6楼 wd339092886 2012-10-12 14:09发表 [回复]
不行不行,我还是倾向于编程。。
5楼 dyx心心 2012-10-12 11:32发表 [回复]
楼主是搞ACM的?
Re: MetalSeed 2012-10-12 23:38发表 [回复]
回复dyx404514:ACM弱菜一枚.. 正在努力中..
Re: dyx心心 2012-11-19 09:37发表 [回复]
回复MetalSeed:我感觉最后那道题,hdu4267那个sum就是个标记而已,不需要
L[step].sum+=num; 只要 L[step].sum=1,就行了吧
4楼 c_006 2012-10-11 11:31发表 [回复]
mark一下。楼主的编译软件是什么的?这么花花绿绿的vs2010?
Re: mz31098 2015-08-22 03:10发表 [回复]
回复c_006:看着像sublime
Re: hellosijian 2012-10-11 19:46发表 [回复]
回复c_006:应该是vim
Re: hello_world_ww 2013-01-26 09:34发表 [回复]
回复hellosijian:GCC/G++
3楼 jiangkaii 2012-10-11 07:32发表 [回复]
进首页了!!
Re: MetalSeed 2012-10-11 10:51发表 [回复]
回复jiangkaii:是蒋凯么。。。
Re: jiangkaii 2012-10-12 01:51发表 [回复]
回复MetalSeed:是的,
2楼 pl___ 2012-10-09 16:16发表 [回复]
手动实现std::map ?
Re: MetalSeed 2012-10-09 17:36发表 [回复]
回复pl___:非也。
map是用红黑树实现的,特点是插入时按照键值自动排序
1楼 Dstnoe 2012-10-08 23:22发表 [回复]
请问,线段树和B树在应用上有什么特别之处?
Re: MetalSeed 2012-10-09 17:38发表 [回复]
回复d1x2p3:不好意思..
B树只有耳闻...
没详细研究过... 
还得努力呢
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
核心技术类目
全部主题 Hadoop AWS 移动游戏 Java Android iOS Swift 智能硬件 Docker OpenStack VPN Spark ERP IE10Eclipse CRM JavaScript 数据库 Ubuntu NFC WAP jQuery BI HTML5 Spring Apache .NET API HTML SDK IISFedora XML LBS Unity Splashtop UML components Windows Mobile Rails QEMU KDE Cassandra CloudStack FTCcoremail OPhone CouchBase 云计算 iOS6 Rackspace Web App SpringSide Maemo Compuware 大数据 aptech PerlTornado Ruby Hibernate ThinkPHP HBase Pure Solr Angular Cloud Foundry Redis Scala Django Bootstrap
  • 个人资料
  •  
    MetalSeed
     
    • 访问:377640次
    • 积分:4504
    • 等级: 
    • 排名:第4894名
    • 原创:109篇
    • 转载:22篇
    • 译文:2篇
    • 评论:154条
  • 文章分类
  • 处理器Ambarella(1)
  • 嵌入式Android(5)
  • 嵌入式linux(20)
  • STM32笔记(14)
  • ACM回忆(47)
  • MCU相关(27)
  • 随笔 小记(2)
  • 东西 小站(6)
  • 文章存档
    • 2016年07月(1)
    • 2015年06月(1)
    • 2015年05月(1)
    • 2015年04月(3)
    • 2015年03月(4)
      展开
  • 阅读排行
  • 数据结构专题——线段树(65567)
  • 主席树介绍(26904)
  • NE555 + CD4017流水灯(21772)
  • Android蓝牙串口通信模板及demo,trick(17010)
  • 两款主流摄像头OV7620与OV7670 By Demok(15269)
  • 锁存器使用总结(9929)
  • 各种音频视频编码方法(6842)
  • D/A与A/D转换器(6457)
  • STM32F407 Discovery uart1串口通信(6088)
  • 基于51的爱心流水灯源码(5203)
  • 评论排行
  • 数据结构专题——线段树(40)
  • 主席树介绍(14)
  • Android蓝牙串口通信模板及demo,trick(11)
  • 天马行空的ACM现场赛回顾(8)
  • 51单片机进阶(6)
  • 51单片机导论(6)
  • STM32F051 IAP源码分享(6)
  • 基于51的爱心流水灯源码(5)
  • 基于RS485的简单现场总线通信系统设计-南邮自动化课程设计(4)
  • C代码优化方案(4)
  • 推荐文章
    • * 程序员10月书讯,评论得书
    • * Android中Xposed框架篇---修改系统位置信息实现自身隐藏功能
    • * Chromium插件(Plugin)模块(Module)加载过程分析
    • * Android TV开发总结--构建一个TV app的直播节目实例
    • * 架构设计:系统存储--MySQL简单主从方案及暴露的问题
  • 最新评论

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/509132.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

UE4加载osgb倾斜摄影数据

在UE4引擎中动态加载调度osgb倾斜摄影数据&#xff0c;利用多线程动态加载瓦片数据&#xff0c;可以顺畅加载海量的瓦片数据。最大可加载200G以上数量级。 Unity3D加载osgb倾斜摄影数据:Unity3d(UE4)动态加载osgb倾斜摄影数据_Answer-3的博客-CSDN博客_unity加载osgb qq:1749…

Unity/UE读取OPC UA和OPC DA数据(UE4)

Unity/UE4通过OPC UA和OPC DA协议读取PLC数据&#xff0c;通过采集服务将数据采集到数据库中&#xff0c;Unity3d和UE4再从数据库中读取数据进行展示&#xff0c;用于三维数字孪生系统接入自动化系统的硬件数据。支持WinCC,组态王&#xff0c;Kepware等组态软件的数据接入。 W…

Unity3d(UE4)动态加载osgb倾斜摄影数据

在Unity3D平台动态加载调度倾斜摄影数据&#xff0c;利用多线程动态加载瓦片数据&#xff0c;可以顺畅加载海量的瓦片数据。目前测试可流畅加载200G左右数据&#xff0c;支持加载本地数据&#xff0c;数据可不放在Unity工程内&#xff0c;也可以将数据放置在服务器上实现网络加…

RMQ算法

RMQ算法 标签&#xff1a; 算法querytable2012-08-28 20:53 14613人阅读 评论(7) 收藏 举报分类&#xff1a;nyist&#xff08;26&#xff09; 数据结构&#xff08;5&#xff09; 1. 概述 RMQ&#xff08;Range Minimum/Maximum Query&#xff09;&#xff0c;即区间最值查询…

bellman ford 算法 判断是否存在负环

Flyer 目录视图摘要视图订阅 微信小程序实战项目——点餐系统 程序员11月书讯&#xff0c;评论得书啦 Get IT技能知识库&#xff0c;50个领域一键直达关闭bellman ford 算法 2013-05-25 15:36 11148人阅读 评论(0) 收藏 举报分类&#xff1a;ACM&#xff08;11&a…

C++ vector用法

C vector用法 在c中&#xff0c;vector是一个十分有用的容器&#xff0c;下面对这个容器做一下总结。 1 基本操作 (1)头文件#include<vector>. (2)创建vector对象&#xff0c;vector<int> vec; (3)尾部插入数字&#xff1a;vec.push_back(a); (4)使用下标访问元素&…

字典树(Trie树)

字典树(Trie树)字典树&#xff0c;又称单词查找树&#xff0c;Trie树&#xff0c;是一种树形结构&#xff0c;典型应用是用于统计&#xff0c;排序和保存大量的字符串&#xff0c;所以经常被搜索引擎系统用于文本词频统计。它的优点是&#xff1a;利用字符串的公共前缀来节约存…

网络流之最大流算法(EdmondsKarp)

网络流之最大流算法&#xff08;EdmondsKarp&#xff09; 标签&#xff1a; 网络流算法EdmondsKarp流量最大流2014-03-11 18:05 34795人阅读 评论(12) 收藏 举报分类&#xff1a;图论~~网络流&#xff08;26&#xff09; 版权声明&#xff1a;本文为博主原创文章&#xff0c;未…

set 用法

Set常用用法 2013-04-22 19:24 86508人阅读 评论(1) 收藏 举报分类&#xff1a;CPlus&#xff08;54&#xff09; set集合容器&#xff1a;实现了红黑树的平衡二叉检索树的数据结构&#xff0c;插入元素时&#xff0c;它会自动调整二叉树的排列&#xff0c;把元素放到适当的位置…

C语言用递归求斐波那契数,让你发现递归的缺陷和效率瓶颈

C语言用递归求斐波那契数&#xff0c;让你发现递归的缺陷和效率瓶颈 分享到&#xff1a;QQ空间新浪微博腾讯微博豆瓣人人网递归是一种强有力的技巧&#xff0c;但和其他技巧一样&#xff0c;它也可能被误用。一般需要递归解决的问题有两个特点&#xff1a;存在限制条件&#xf…

C语言中文件的读取和写入

C语言中文件的读取和写入 注意&#xff1a; 1、由于C是缓冲写 所以要在关闭或刷新后才能看到文件内容 2、电脑处理文本型和二进制型的不同 &#xff08;因为电脑只认识二进制格式&#xff09; 在C语言中写文件 //获取文件指针 FILE *pFile fopen("1.txt", //打开文件…

mysql 常用操作

mysql 常用操作&#xff08;整理&#xff09; 标签&#xff1a; MYSQL常用操作 2013-07-22 16:37 10846人阅读 评论(0) 收藏 举报 本文章已收录于&#xff1a; 分类&#xff1a; MYSQL 版权声明&#xff1a;本文为博主原创文章&#xff0c;未经博主允许不得转载。 目录(?)[]…

deque

STL之deque容器详解Deque 容器 deque容器是C标准模版库(STL,Standard Template Library)中的部分内容。deque容器类与vector类似&#xff0c;支持随机访问和快速插入删除&#xff0c;它在容器中某一位置上的操作所花费的是线性时间。与vector不同的是&#xff0c;deque还支持从…

RMQ算法,求区间最值

poj 3264 Balanced Lineup 2016-07-27 11:15 49人阅读 评论(0) 收藏 举报分类&#xff1a;RMQ(Range MinimumMaximum Quer)&#xff08;4&#xff09; Balanced LineupTime Limit: 5000MS Memory Limit: 65536KTotal Submissions: 45854 Accepted: 21536Case Time Limit: 2000M…

【转载】向量空间模型VSM及余弦计算

向量空间模型VSM及余弦计算 向量空间模型的基本思想是把文档简化为以特征项&#xff08;关键词&#xff09;的权重为分量的N维向量表示。 这个模型假设词与词间不相关&#xff08;这个前提造成这个模型无法进行语义相关的判断&#xff0c;向量空间模型的缺点在于关键词之间的…

【美文保存】nosql数据库对比以及如何巧妙利用redis来提高效率?

1. MySqlMemcached架构的问题 实际MySQL是适合进行海量数据存储的&#xff0c;通过Memcached将热点数据加载到cache&#xff0c;加速访问&#xff0c;很多公司都曾经使用过这样的架构&#xff0c;但随着业务数据量的不断增加&#xff0c;和访问量的持续增长&#xff0c;我们遇…

嵌入式开发——DMA外设到内存

学习目标 加强理解DMA数据传输过程加强掌握DMA的初始化流程掌握DMA数据表查询理解源和目标的配置理解数据传输特点能够动态配置源数据学习内容 需求 uint8_t data; 串口接收(&data);data有数据了 实现串口的数据接收,要求采用dma的方式。 数据交互流程 CPU配置好DMA外…

系统设计类问题

如果让你设计一个秒杀系统&#xff0c;你会如何设计&#xff1f; Redis是一个分布式缓存系统&#xff0c;支持多种数据结构&#xff0c;我们可以利用Redis轻松实现一个强大的秒杀系统。 我们可以采用Redis 最简单的key-value数据结构&#xff0c;用一个原子类型的变量值(Atomic…

一种基于邻域的聚类算法

基本概念&#xff1a; 给定数据集D {d1&#xff0c;d2 ,.. &#xff0c;dn}&#xff0c;p和q是D中的两个任意对象。我们使用欧氏距离来评估p和q之间的距离&#xff0c;表示为 dist&#xff08;p&#xff0c;q&#xff09;。 我们将首先给出k-最近邻集合和反向的定义k-最近邻集…

博弈之威佐夫

博弈之威佐夫博弈详解 标签&#xff1a; 数学博弈游戏威佐夫algorithm2014-03-21 10:40 5330人阅读 评论(1) 收藏 举报分类&#xff1a;博弈&#xff08;6&#xff09; 版权声明&#xff1a;本文为博主原创文章&#xff0c;未经博主允许不得转载。 威佐夫博弈是博弈中的另一个经…