原文出处:http://www.notonlysuccess.com/

今晚上比赛就考到了 排兵布阵啊,难受。

【完全版】线段树

很早前写的那篇线段树专辑至今一直是本博客阅读点击量最大的一片文章,当时觉得挺自豪的,还去pku打广告,但是现在我自己都不太好意思去看那篇文章了,觉得当时的代码风格实在是太丑了,很多线段树的初学者可能就是看着这篇文章来练习的,如果不小心被我培养出了这么糟糕的风格,实在是过意不去,正好过几天又要给集训队讲解线段树,所以决定把这些题目重新写一遍,顺便把近年我接触到的一些新题更新上去~;并且学习了splay等更高级的数据结构后对线段树的体会有更深了一层,线段树的写法也就比以前飘逸,简洁且方便多了.

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

  • 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:区间求和

  1 #include <cstdio>
  2
  3
  4
  5 #define lson l , m , rt << 1
  6
  7 #define rson m + 1 , r , rt << 1 | 1
  8
  9 const int maxn = 55555;
 10
 11 int sum[maxn<<2];
 12
 13 void PushUP(int rt) {
 14
 15        sum[rt] = sum[rt<<1] + sum[rt<<1|1];
 16
 17 }
 18
 19 void build(int l,int r,int rt) {
 20
 21        if (l == r) {
 22
 23               scanf("%d",&sum[rt]);
 24
 25               return ;
 26
 27        }
 28
 29        int m = (l + r) >> 1;
 30
 31        build(lson);
 32
 33        build(rson);
 34
 35        PushUP(rt);
 36
 37 }
 38
 39 void update(int p,int add,int l,int r,int rt) {
 40
 41        if (l == r) {
 42
 43               sum[rt] += add;
 44
 45               return ;
 46
 47        }
 48
 49        int m = (l + r) >> 1;
 50
 51        if (p <= m) update(p , add , lson);
 52
 53        else update(p , add , rson);
 54
 55        PushUP(rt);
 56
 57 }
 58
 59 int query(int L,int R,int l,int r,int rt) {
 60
 61        if (L <= l && r <= R) {
 62
 63               return sum[rt];
 64
 65        }
 66
 67        int m = (l + r) >> 1;
 68
 69        int ret = 0;
 70
 71        if (L <= m) ret += query(L , R , lson);
 72
 73        if (R > m) ret += query(L , R , rson);
 74
 75        return ret;
 76
 77 }
 78
 79 int main() {
 80
 81        int T , n;
 82
 83        scanf("%d",&T);
 84
 85        for (int cas = 1 ; cas <= T ; cas ++) {
 86
 87               printf("Case %d:\n",cas);
 88
 89               scanf("%d",&n);
 90
 91               build(1 , n , 1);
 92
 93               char op[10];
 94
 95               while (scanf("%s",op)) {
 96
 97                      if (op[0] == 'E') break;
 98
 99                      int a , b;
100
101                      scanf("%d%d",&a,&b);
102
103                      if (op[0] == 'Q') printf("%d\n",query(a , b , 1 , n , 1));
104
105                      else if (op[0] == 'S') update(a , -b , 1 , n , 1);
106
107                      else update(a , b , 1 , n , 1);
108
109               }
110
111        }
112
113        return 0;
114
115 }

  

  

hdu1754 I Hate It

题意:O(-1)

思路:O(-1)

线段树功能:update:单点替换 query:区间最值

  

  1 #include <cstdio>
  2
  3 #include <algorithm>
  4
  5 using namespace std;
  6
  7
  8
  9 #define lson l , m , rt << 1
 10
 11 #define rson m + 1 , r , rt << 1 | 1
 12
 13 const int maxn = 222222;
 14
 15 int MAX[maxn<<2];
 16
 17 void PushUP(int rt) {
 18
 19        MAX[rt] = max(MAX[rt<<1] , MAX[rt<<1|1]);
 20
 21 }
 22
 23 void build(int l,int r,int rt) {
 24
 25        if (l == r) {
 26
 27               scanf("%d",&MAX[rt]);
 28
 29               return ;
 30
 31        }
 32
 33        int m = (l + r) >> 1;
 34
 35        build(lson);
 36
 37        build(rson);
 38
 39        PushUP(rt);
 40
 41 }
 42
 43 void update(int p,int sc,int l,int r,int rt) {
 44
 45        if (l == r) {
 46
 47               MAX[rt] = sc;
 48
 49               return ;
 50
 51        }
 52
 53        int m = (l + r) >> 1;
 54
 55        if (p <= m) update(p , sc , lson);
 56
 57        else update(p , sc , rson);
 58
 59        PushUP(rt);
 60
 61 }
 62
 63 int query(int L,int R,int l,int r,int rt) {
 64
 65        if (L <= l && r <= R) {
 66
 67               return MAX[rt];
 68
 69        }
 70
 71        int m = (l + r) >> 1;
 72
 73        int ret = 0;
 74
 75        if (L <= m) ret = max(ret , query(L , R , lson));
 76
 77        if (R > m) ret = max(ret , query(L , R , rson));
 78
 79        return ret;
 80
 81 }
 82
 83 int main() {
 84
 85        int n , m;
 86
 87        while (~scanf("%d%d",&n,&m)) {
 88
 89               build(1 , n , 1);
 90
 91               while (m --) {
 92
 93                      char op[2];
 94
 95                      int a , b;
 96
 97                      scanf("%s%d%d",op,&a,&b);
 98
 99                      if (op[0] == 'Q') printf("%d\n",query(a , b , 1 , n , 1));
100
101                      else update(a , b , 1 , n , 1);
102
103               }
104
105        }
106
107        return 0;
108
109 }

  

  

hdu1394 Minimum Inversion Number

题意:求Inversion后的最小逆序数

思路:用O(nlogn)复杂度求出最初逆序数后,就可以用O(1)的复杂度分别递推出其他解

线段树功能:update:单点增减 query:区间求和

  1 #include <cstdio>
  2
  3 #include <algorithm>
  4
  5 using namespace std;
  6
  7
  8
  9 #define lson l , m , rt << 1
 10
 11 #define rson m + 1 , r , rt << 1 | 1
 12
 13 const int maxn = 5555;
 14
 15 int sum[maxn<<2];
 16
 17 void PushUP(int rt) {
 18
 19        sum[rt] = sum[rt<<1] + sum[rt<<1|1];
 20
 21 }
 22
 23 void build(int l,int r,int rt) {
 24
 25        sum[rt] = 0;
 26
 27        if (l == r) return ;
 28
 29        int m = (l + r) >> 1;
 30
 31        build(lson);
 32
 33        build(rson);
 34
 35 }
 36
 37 void update(int p,int l,int r,int rt) {
 38
 39        if (l == r) {
 40
 41               sum[rt] ++;
 42
 43               return ;
 44
 45        }
 46
 47        int m = (l + r) >> 1;
 48
 49        if (p <= m) update(p , lson);
 50
 51        else update(p , rson);
 52
 53        PushUP(rt);
 54
 55 }
 56
 57 int query(int L,int R,int l,int r,int rt) {
 58
 59        if (L <= l && r <= R) {
 60
 61               return sum[rt];
 62
 63        }
 64
 65        int m = (l + r) >> 1;
 66
 67        int ret = 0;
 68
 69        if (L <= m) ret += query(L , R , lson);
 70
 71        if (R > m) ret += query(L , R , rson);
 72
 73        return ret;
 74
 75 }
 76
 77 int x[maxn];
 78
 79 int main() {
 80
 81        int n;
 82
 83        while (~scanf("%d",&n)) {
 84
 85               build(0 , n - 1 , 1);
 86
 87               int sum = 0;
 88
 89               for (int i = 0 ; i < n ; i ++) {
 90
 91                      scanf("%d",&x[i]);
 92
 93                      sum += query(x[i] , n - 1 , 0 , n - 1 , 1);
 94
 95                      update(x[i] , 0 , n - 1 , 1);
 96
 97               }
 98
 99               int ret = sum;
100
101               for (int i = 0 ; i < n ; i ++) {
102
103                      sum += n - x[i] - x[i] - 1;
104
105                      ret = min(ret , sum);
106
107               }
108
109               printf("%d\n",ret);
110
111        }
112
113        return 0;
114
115 }

  

  

hdu2795 Billboard

题意:h*w的木板,放进一些1*L的物品,求每次放空间能容纳且最上边的位子

思路:每次找到最大值的位子,然后减去L

线段树功能:query:区间求最大值的位子(直接把update的操作在query里做了)

  

 1 #include <cstdio>
 2
 3 #include <algorithm>
 4
 5 using namespace std;
 6
 7
 8
 9 #define lson l , m , rt << 1
10
11 #define rson m + 1 , r , rt << 1 | 1
12
13 const int maxn = 222222;
14
15 int h , w , n;
16
17 int MAX[maxn<<2];
18
19 void PushUP(int rt) {
20
21        MAX[rt] = max(MAX[rt<<1] , MAX[rt<<1|1]);
22
23 }
24
25 void build(int l,int r,int rt) {
26
27        MAX[rt] = w;
28
29        if (l == r) return ;
30
31        int m = (l + r) >> 1;
32
33        build(lson);
34
35        build(rson);
36
37 }
38
39 int query(int x,int l,int r,int rt) {
40
41        if (l == r) {
42
43               MAX[rt] -= x;
44
45               return l;
46
47        }
48
49        int m = (l + r) >> 1;
50
51        int ret = (MAX[rt<<1] >= x) ? query(x , lson) : query(x , rson);
52
53        PushUP(rt);
54
55        return ret;
56
57 }
58
59 int main() {
60
61        while (~scanf("%d%d%d",&h,&w,&n)) {
62
63               if (h > n) h = n;
64
65               build(1 , h , 1);
66
67               while (n --) {
68
69                      int x;
70
71                      scanf("%d",&x);
72
73                      if (MAX[1] < x) puts("-1");
74
75                      else printf("%d\n",query(x , 1 , h , 1));
76
77               }
78
79        }
80
81        return 0;
82
83 }
84  

  

  

练习:

poj2828 Buy Tickets

poj2886 Who Gets the Most Candies?

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

hdu1698 Just a Hook

题意:O(-1)

思路:O(-1)

线段树功能:update:成段替换 (由于只query一次总区间,所以可以直接输出1结点的信息)

  1 #include <cstdio>
  2
  3 #include <algorithm>
  4
  5 using namespace std;
  6
  7
  8
  9 #define lson l , m , rt << 1
 10
 11 #define rson m + 1 , r , rt << 1 | 1
 12
 13 const int maxn = 111111;
 14
 15 int h , w , n;
 16
 17 int col[maxn<<2];
 18
 19 int sum[maxn<<2];
 20
 21 void PushUp(int rt) {
 22
 23        sum[rt] = sum[rt<<1] + sum[rt<<1|1];
 24
 25 }
 26
 27 void PushDown(int rt,int m) {
 28
 29        if (col[rt]) {
 30
 31               col[rt<<1] = col[rt<<1|1] = col[rt];
 32
 33               sum[rt<<1] = (m - (m >> 1)) * col[rt];
 34
 35               sum[rt<<1|1] = (m >> 1) * col[rt];
 36
 37               col[rt] = 0;
 38
 39        }
 40
 41 }
 42
 43 void build(int l,int r,int rt) {
 44
 45        col[rt] = 0;
 46
 47        sum[rt] = 1;
 48
 49        if (l == r) return ;
 50
 51        int m = (l + r) >> 1;
 52
 53        build(lson);
 54
 55        build(rson);
 56
 57        PushUp(rt);
 58
 59 }
 60
 61 void update(int L,int R,int c,int l,int r,int rt) {
 62
 63        if (L <= l && r <= R) {
 64
 65               col[rt] = c;
 66
 67               sum[rt] = c * (r - l + 1);
 68
 69               return ;
 70
 71        }
 72
 73        PushDown(rt , r - l + 1);
 74
 75        int m = (l + r) >> 1;
 76
 77        if (L <= m) update(L , R , c , lson);
 78
 79        if (R > m) update(L , R , c , rson);
 80
 81        PushUp(rt);
 82
 83 }
 84
 85 int main() {
 86
 87        int T , n , m;
 88
 89        scanf("%d",&T);
 90
 91        for (int cas = 1 ; cas <= T ; cas ++) {
 92
 93               scanf("%d%d",&n,&m);
 94
 95               build(1 , n , 1);
 96
 97               while (m --) {
 98
 99                      int a , b , c;
100
101                      scanf("%d%d%d",&a,&b,&c);
102
103                      update(a , b , c , 1 , n , 1);
104
105               }
106
107               printf("Case %d: The total value of the hook is %d.\n",cas , sum[1]);
108
109        }
110
111        return 0;
112
113 }

poj3468 A Simple Problem with Integers

题意:O(-1)

思路:O(-1)

线段树功能:update:成段增减 query:区间求和

  

  1 #include <cstdio>
  2
  3 #include <algorithm>
  4
  5 using namespace std;
  6
  7
  8
  9 #define lson l , m , rt << 1
 10
 11 #define rson m + 1 , r , rt << 1 | 1
 12
 13 #define LL long long
 14
 15 const int maxn = 111111;
 16
 17 LL add[maxn<<2];
 18
 19 LL sum[maxn<<2];
 20
 21 void PushUp(int rt) {
 22
 23        sum[rt] = sum[rt<<1] + sum[rt<<1|1];
 24
 25 }
 26
 27 void PushDown(int rt,int m) {
 28
 29        if (add[rt]) {
 30
 31               add[rt<<1] += add[rt];
 32
 33               add[rt<<1|1] += add[rt];
 34
 35               sum[rt<<1] += add[rt] * (m - (m >> 1));
 36
 37               sum[rt<<1|1] += add[rt] * (m >> 1);
 38
 39               add[rt] = 0;
 40
 41        }
 42
 43 }
 44
 45 void build(int l,int r,int rt) {
 46
 47        add[rt] = 0;
 48
 49        if (l == r) {
 50
 51               scanf("%lld",&sum[rt]);
 52
 53               return ;
 54
 55        }
 56
 57        int m = (l + r) >> 1;
 58
 59        build(lson);
 60
 61        build(rson);
 62
 63        PushUp(rt);
 64
 65 }
 66
 67 void update(int L,int R,int c,int l,int r,int rt) {
 68
 69        if (L <= l && r <= R) {
 70
 71               add[rt] += c;
 72
 73               sum[rt] += (LL)c * (r - l + 1);
 74
 75               return ;
 76
 77        }
 78
 79        PushDown(rt , r - l + 1);
 80
 81        int m = (l + r) >> 1;
 82
 83        if (L <= m) update(L , R , c , lson);
 84
 85        if (m < R) update(L , R , c , rson);
 86
 87        PushUp(rt);
 88
 89 }
 90
 91 LL query(int L,int R,int l,int r,int rt) {
 92
 93        if (L <= l && r <= R) {
 94
 95               return sum[rt];
 96
 97        }
 98
 99        PushDown(rt , r - l + 1);
100
101        int m = (l + r) >> 1;
102
103        LL ret = 0;
104
105        if (L <= m) ret += query(L , R , lson);
106
107        if (m < R) ret += query(L , R , rson);
108
109        return ret;
110
111 }
112
113 int main() {
114
115        int N , Q;
116
117        scanf("%d%d",&N,&Q);
118
119        build(1 , N , 1);
120
121        while (Q --) {
122
123               char op[2];
124
125               int a , b , c;
126
127               scanf("%s",op);
128
129               if (op[0] == 'Q') {
130
131                      scanf("%d%d",&a,&b);
132
133                      printf("%lld\n",query(a , b , 1 , N , 1));
134
135               } else {
136
137                      scanf("%d%d%d",&a,&b,&c);
138
139                      update(a , b , c , 1 , N , 1);
140
141               }
142
143        }
144
145        return 0;
146
147 }

  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,2,6,10]

如果相邻数字间距大于1的话,在其中加上任意一个数字,比如加成[1,2,3,6,7,10],然后再做线段树就好了.

线段树功能:update:成段替换 query:简单hash

  

  1 #include <cstdio>
  2
  3 #include <cstring>
  4
  5 #include <algorithm>
  6
  7 using namespace std;
  8
  9 #define lson l , m , rt << 1
 10
 11 #define rson m + 1 , r , rt << 1 | 1
 12
 13
 14
 15 const int maxn = 11111;
 16
 17 bool hash[maxn];
 18
 19 int li[maxn] , ri[maxn];
 20
 21 int X[maxn*3];
 22
 23 int col[maxn<<4];
 24
 25 int cnt;
 26
 27
 28
 29 void PushDown(int rt) {
 30
 31        if (col[rt] != -1) {
 32
 33               col[rt<<1] = col[rt<<1|1] = col[rt];
 34
 35               col[rt] = -1;
 36
 37        }
 38
 39 }
 40
 41 void update(int L,int R,int c,int l,int r,int rt) {
 42
 43        if (L <= l && r <= R) {
 44
 45               col[rt] = c;
 46
 47               return ;
 48
 49        }
 50
 51        PushDown(rt);
 52
 53        int m = (l + r) >> 1;
 54
 55        if (L <= m) update(L , R , c , lson);
 56
 57        if (m < R) update(L , R , c , rson);
 58
 59 }
 60
 61 void query(int l,int r,int rt) {
 62
 63        if (col[rt] != -1) {
 64
 65               if (!hash[col[rt]]) cnt ++;
 66
 67               hash[ col[rt] ] = true;
 68
 69               return ;
 70
 71        }
 72
 73        if (l == r) return ;
 74
 75        int m = (l + r) >> 1;
 76
 77        query(lson);
 78
 79        query(rson);
 80
 81 }
 82
 83 int Bin(int key,int n,int X[]) {
 84
 85        int l = 0 , r = n - 1;
 86
 87        while (l <= r) {
 88
 89               int m = (l + r) >> 1;
 90
 91               if (X[m] == key) return m;
 92
 93               if (X[m] < key) l = m + 1;
 94
 95               else r = m - 1;
 96
 97        }
 98
 99        return -1;
100
101 }
102
103 int main() {
104
105        int T , n;
106
107        scanf("%d",&T);
108
109        while (T --) {
110
111               scanf("%d",&n);
112
113               int nn = 0;
114
115               for (int i = 0 ; i < n ; i ++) {
116
117                      scanf("%d%d",&li[i] , &ri[i]);
118
119                      X[nn++] = li[i];
120
121                      X[nn++] = ri[i];
122
123               }
124
125               sort(X , X + nn);
126
127               int m = 1;
128
129               for (int i = 1 ; i < nn; i ++) {
130
131                      if (X[i] != X[i-1]) X[m ++] = X[i];
132
133               }
134
135               for (int i = m - 1 ; i > 0 ; i --) {
136
137                      if (X[i] != X[i-1] + 1) X[m ++] = X[i] + 1;
138
139               }
140
141               sort(X , X + m);
142
143               memset(col , -1 , sizeof(col));
144
145               for (int i = 0 ; i < n ; i ++) {
146
147                      int l = Bin(li[i] , m , X);
148
149                      int r = Bin(ri[i] , m , X);
150
151                      update(l , r , i , 0 , m , 1);
152
153               }
154
155               cnt = 0;
156
157               memset(hash , false , sizeof(hash));
158
159               query(0 , m , 1);
160
161               printf("%d\n",cnt);
162
163        }
164
165        return 0;
166
167 }

  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

  

  1 #include <cstdio>
  2
  3 #include <cstring>
  4
  5 #include <cctype>
  6
  7 #include <algorithm>
  8
  9 using namespace std;
 10
 11 #define lson l , m , rt << 1
 12
 13 #define rson m + 1 , r , rt << 1 | 1
 14
 15
 16
 17 const int maxn = 131072;
 18
 19 bool hash[maxn];
 20
 21 int cover[maxn<<2];
 22
 23 int XOR[maxn<<2];
 24
 25 void FXOR(int rt) {
 26
 27        if (cover[rt] != -1) cover[rt] ^= 1;
 28
 29        else XOR[rt] ^= 1;
 30
 31 }
 32
 33 void PushDown(int rt) {
 34
 35        if (cover[rt] != -1) {
 36
 37               cover[rt<<1] = cover[rt<<1|1] = cover[rt];
 38
 39               XOR[rt<<1] = XOR[rt<<1|1] = 0;
 40
 41               cover[rt] = -1;
 42
 43        }
 44
 45        if (XOR[rt]) {
 46
 47               FXOR(rt<<1);
 48
 49               FXOR(rt<<1|1);
 50
 51               XOR[rt] = 0;
 52
 53        }
 54
 55 }
 56
 57 void update(char op,int L,int R,int l,int r,int rt) {
 58
 59        if (L <= l && r <= R) {
 60
 61               if (op == 'U') {
 62
 63                      cover[rt] = 1;
 64
 65                      XOR[rt] = 0;
 66
 67               } else if (op == 'D') {
 68
 69                      cover[rt] = 0;
 70
 71                      XOR[rt] = 0;
 72
 73               } else if (op == 'C' || op == 'S') {
 74
 75                      FXOR(rt);
 76
 77               }
 78
 79               return ;
 80
 81        }
 82
 83        PushDown(rt);
 84
 85        int m = (l + r) >> 1;
 86
 87        if (L <= m) update(op , L , R , lson);
 88
 89        else if (op == 'I' || op == 'C') {
 90
 91               XOR[rt<<1] = cover[rt<<1] = 0;
 92
 93        }
 94
 95        if (m < R) update(op , L , R , rson);
 96
 97        else if (op == 'I' || op == 'C') {
 98
 99               XOR[rt<<1|1] = cover[rt<<1|1] = 0;
100
101        }
102
103 }
104
105 void query(int l,int r,int rt) {
106
107        if (cover[rt] == 1) {
108
109               for (int it = l ; it <= r ; it ++) {
110
111                      hash[it] = true;
112
113               }
114
115               return ;
116
117        } else if (cover[rt] == 0) return ;
118
119        if (l == r) return ;
120
121        PushDown(rt);
122
123        int m = (l + r) >> 1;
124
125        query(lson);
126
127        query(rson);
128
129 }
130
131 int main() {
132
133        cover[1] = XOR[1] = 0;
134
135        char op , l , r;
136
137        int a , b;
138
139        while ( ~scanf("%c %c%d,%d%c\n",&op , &l , &a , &b , &r) ) {
140
141               a <<= 1 , b <<= 1;
142
143               if (l == '(') a ++;
144
145               if (r == ')') b --;
146
147               if (a > b) {
148
149                      if (op == 'C' || op == 'I') {
150
151                             cover[1] = XOR[1] = 0;
152
153                      }
154
155               } else update(op , a , b , 0 , maxn , 1);
156
157        }
158
159        query(0 , maxn , 1);
160
161        bool flag = false;
162
163        int s = -1 , e;
164
165        for (int i = 0 ; i <= maxn ; i ++) {
166
167               if (hash[i]) {
168
169                      if (s == -1) s = i;
170
171                      e = i;
172
173               } else {
174
175                      if (s != -1) {
176
177                             if (flag) printf(" ");
178
179                             flag = true;
180
181                             printf("%c%d,%d%c",s&1?'(':'[' , s>>1 , (e+1)>>1 , e&1?')':']');
182
183                             s = -1;
184
185                      }
186
187               }
188
189        }
190
191        if (!flag) printf("empty set");
192
193        puts("");
194
195        return 0;
196
197 }
198  

  

 练习:

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:询问满足条件的最左断点

  

  1 #include <cstdio>
  2
  3 #include <cstring>
  4
  5 #include <cctype>
  6
  7 #include <algorithm>
  8
  9 using namespace std;
 10
 11 #define lson l , m , rt << 1
 12
 13 #define rson m + 1 , r , rt << 1 | 1
 14
 15
 16
 17 const int maxn = 55555;
 18
 19 int lsum[maxn<<2] , rsum[maxn<<2] , msum[maxn<<2];
 20
 21 int cover[maxn<<2];
 22
 23
 24
 25 void PushDown(int rt,int m) {
 26
 27        if (cover[rt] != -1) {
 28
 29               cover[rt<<1] = cover[rt<<1|1] = cover[rt];
 30
 31               msum[rt<<1] = lsum[rt<<1] = rsum[rt<<1] = cover[rt] ? 0 : m - (m >> 1);
 32
 33               msum[rt<<1|1] = lsum[rt<<1|1] = rsum[rt<<1|1] = cover[rt] ? 0 : (m >> 1);
 34
 35               cover[rt] = -1;
 36
 37        }
 38
 39 }
 40
 41 void PushUp(int rt,int m) {
 42
 43        lsum[rt] = lsum[rt<<1];
 44
 45        rsum[rt] = rsum[rt<<1|1];
 46
 47        if (lsum[rt] == m - (m >> 1)) lsum[rt] += lsum[rt<<1|1];
 48
 49        if (rsum[rt] == (m >> 1)) rsum[rt] += rsum[rt<<1];
 50
 51        msum[rt] = max(lsum[rt<<1|1] + rsum[rt<<1] , max(msum[rt<<1] , msum[rt<<1|1]));
 52
 53 }
 54
 55 void build(int l,int r,int rt) {
 56
 57        msum[rt] = lsum[rt] = rsum[rt] = r - l + 1;
 58
 59        cover[rt] = -1;
 60
 61        if (l == r) return ;
 62
 63        int m = (l + r) >> 1;
 64
 65        build(lson);
 66
 67        build(rson);
 68
 69 }
 70
 71 void update(int L,int R,int c,int l,int r,int rt) {
 72
 73        if (L <= l && r <= R) {
 74
 75               msum[rt] = lsum[rt] = rsum[rt] = c ? 0 : r - l + 1;
 76
 77               cover[rt] = c;
 78
 79               return ;
 80
 81        }
 82
 83        PushDown(rt , r - l + 1);
 84
 85        int m = (l + r) >> 1;
 86
 87        if (L <= m) update(L , R , c , lson);
 88
 89        if (m < R) update(L , R , c , rson);
 90
 91        PushUp(rt , r - l + 1);
 92
 93 }
 94
 95 int query(int w,int l,int r,int rt) {
 96
 97        if (l == r) return l;
 98
 99        PushDown(rt , r - l + 1);
100
101        int m = (l + r) >> 1;
102
103        if (msum[rt<<1] >= w) return query(w , lson);
104
105        else if (rsum[rt<<1] + lsum[rt<<1|1] >= w) return m - rsum[rt<<1] + 1;
106
107        return query(w , rson);
108
109 }
110
111 int main() {
112
113        int n , m;
114
115        scanf("%d%d",&n,&m);
116
117        build(1 , n , 1);
118
119        while (m --) {
120
121               int op , a , b;
122
123               scanf("%d",&op);
124
125               if (op == 1) {
126
127                      scanf("%d",&a);
128
129                      if (msum[1] < a) puts("0");
130
131                      else {
132
133                             int p = query(a , 1 , n , 1);
134
135                             printf("%d\n",p);
136
137                             update(p , p + a - 1 , 1 , 1 , n , 1);
138
139                      }
140
141               } else {
142
143                      scanf("%d%d",&a,&b);
144
145                      update(a , a + b - 1 , 0 , 1 , n , 1);
146
147               }
148
149        }
150
151        return 0;
152
153 }

  

练习:

hdu3308 LCIS

hdu3397 Sequence operation

hdu2871 Memory Control

hdu1540 Tunnel Warfare

CF46-D Parking Lot

扫描线

这类题目需要将一些操作排序,然后从左到右用一根扫描线(当然是在我们脑子里)扫过去

最典型的就是矩形面积并,周长并等题

hdu1542 Atlantis

题意:矩形面积并

思路:浮点数先要离散化;然后把矩形分成两条边,上边和下边,对横轴建树,然后从下到上扫描上去,用cnt表示该区间下边比上边多几个

线段树操作:update:区间增减 query:直接取根节点的值

  

  1 #include <cstdio>
  2
  3 #include <cstring>
  4
  5 #include <cctype>
  6
  7 #include <algorithm>
  8
  9 using namespace std;
 10
 11 #define lson l , m , rt << 1
 12
 13 #define rson m + 1 , r , rt << 1 | 1
 14
 15
 16
 17 const int maxn = 2222;
 18
 19 int cnt[maxn << 2];
 20
 21 double sum[maxn << 2];
 22
 23 double X[maxn];
 24
 25 struct Seg {
 26
 27        double h , l , r;
 28
 29        int s;
 30
 31        Seg(){}
 32
 33        Seg(double a,double b,double c,int d) : l(a) , r(b) , h(c) , s(d) {}
 34
 35        bool operator < (const Seg &cmp) const {
 36
 37               return h < cmp.h;
 38
 39        }
 40
 41 }ss[maxn];
 42
 43 void PushUp(int rt,int l,int r) {
 44
 45        if (cnt[rt]) sum[rt] = X[r+1] - X[l];
 46
 47        else if (l == r) sum[rt] = 0;
 48
 49        else sum[rt] = sum[rt<<1] + sum[rt<<1|1];
 50
 51 }
 52
 53 void update(int L,int R,int c,int l,int r,int rt) {
 54
 55        if (L <= l && r <= R) {
 56
 57               cnt[rt] += c;
 58
 59               PushUp(rt , l , r);
 60
 61               return ;
 62
 63        }
 64
 65        int m = (l + r) >> 1;
 66
 67        if (L <= m) update(L , R , c , lson);
 68
 69        if (m < R) update(L , R , c , rson);
 70
 71        PushUp(rt , l , r);
 72
 73 }
 74
 75 int Bin(double key,int n,double X[]) {
 76
 77        int l = 0 , r = n - 1;
 78
 79        while (l <= r) {
 80
 81               int m = (l + r) >> 1;
 82
 83               if (X[m] == key) return m;
 84
 85               if (X[m] < key) l = m + 1;
 86
 87               else r = m - 1;
 88
 89        }
 90
 91        return -1;
 92
 93 }
 94
 95 int main() {
 96
 97        int n , cas = 1;
 98
 99        while (~scanf("%d",&n) && n) {
100
101               int m = 0;
102
103               while (n --) {
104
105                      double a , b , c , d;
106
107                      scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
108
109                      X[m] = a;
110
111                      ss[m++] = Seg(a , c , b , 1);
112
113                      X[m] = c;
114
115                      ss[m++] = Seg(a , c , d , -1);
116
117               }
118
119               sort(X , X + m);
120
121               sort(ss , ss + m);
122
123               int k = 1;
124
125               for (int i = 1 ; i < m ; i ++) {
126
127                      if (X[i] != X[i-1]) X[k++] = X[i];
128
129               }
130
131               memset(cnt , 0 , sizeof(cnt));
132
133               memset(sum , 0 , sizeof(sum));
134
135               double ret = 0;
136
137               for (int i = 0 ; i < m - 1 ; i ++) {
138
139                      int l = Bin(ss[i].l , k , X);
140
141                      int r = Bin(ss[i].r , k , X) - 1;
142
143                      if (l <= r) update(l , r , ss[i].s , 0 , k - 1, 1);
144
145                      ret += sum[1] * (ss[i+1].h - ss[i].h);
146
147               }
148
149               printf("Test case #%d\nTotal explored area: %.2lf\n\n",cas++ , ret);
150
151        }
152
153        return 0;
154
155 }

hdu1828 Picture

题意:矩形周长并

思路:与面积不同的地方是还要记录竖的边有几个(numseg记录),并且当边界重合的时候需要合并(用lbd和rbd表示边界来辅助)

线段树操作:update:区间增减 query:直接取根节点的值

  

  1 #include <cstdio>
  2
  3 #include <cstring>
  4
  5 #include <cctype>
  6
  7 #include <algorithm>
  8
  9 using namespace std;
 10
 11 #define lson l , m , rt << 1
 12
 13 #define rson m + 1 , r , rt << 1 | 1
 14
 15
 16
 17 const int maxn = 22222;
 18
 19 struct Seg{
 20
 21        int l , r , h , s;
 22
 23        Seg() {}
 24
 25        Seg(int a,int b,int c,int d):l(a) , r(b) , h(c) , s(d) {}
 26
 27        bool operator < (const Seg &cmp) const {
 28
 29               return h < cmp.h;
 30
 31        }
 32
 33 }ss[maxn];
 34
 35 bool lbd[maxn<<2] , rbd[maxn<<2];
 36
 37 int numseg[maxn<<2];
 38
 39 int cnt[maxn<<2];
 40
 41 int len[maxn<<2];
 42
 43 void PushUP(int rt,int l,int r) {
 44
 45        if (cnt[rt]) {
 46
 47               lbd[rt] = rbd[rt] = 1;
 48
 49               len[rt] = r - l + 1;
 50
 51               numseg[rt] = 2;
 52
 53        } else if (l == r) {
 54
 55               len[rt] = numseg[rt] = lbd[rt] = rbd[rt] = 0;
 56
 57        } else {
 58
 59               lbd[rt] = lbd[rt<<1];
 60
 61               rbd[rt] = rbd[rt<<1|1];
 62
 63               len[rt] = len[rt<<1] + len[rt<<1|1];
 64
 65               numseg[rt] = numseg[rt<<1] + numseg[rt<<1|1];
 66
 67               if (lbd[rt<<1|1] && rbd[rt<<1]) numseg[rt] -= 2;//两条线重合
 68
 69        }
 70
 71 }
 72
 73 void update(int L,int R,int c,int l,int r,int rt) {
 74
 75        if (L <= l && r <= R) {
 76
 77               cnt[rt] += c;
 78
 79               PushUP(rt , l , r);
 80
 81               return ;
 82
 83        }
 84
 85        int m = (l + r) >> 1;
 86
 87        if (L <= m) update(L , R , c , lson);
 88
 89        if (m < R) update(L , R , c , rson);
 90
 91        PushUP(rt , l , r);
 92
 93 }
 94
 95 int main() {
 96
 97        int n;
 98
 99        while (~scanf("%d",&n)) {
100
101               int m = 0;
102
103               int lbd = 10000, rbd = -10000;
104
105               for (int i = 0 ; i < n ; i ++) {
106
107                      int a , b , c , d;
108
109                      scanf("%d%d%d%d",&a,&b,&c,&d);
110
111                      lbd = min(lbd , a);
112
113                      rbd = max(rbd , c);
114
115                      ss[m++] = Seg(a , c , b , 1);
116
117                      ss[m++] = Seg(a , c , d , -1);
118
119               }
120
121               sort(ss , ss + m);
122
123               int ret = 0 , last = 0;
124
125               for (int i = 0 ; i < m ; i ++) {
126
127                      if (ss[i].l < ss[i].r) update(ss[i].l , ss[i].r - 1 , ss[i].s , lbd , rbd - 1 , 1);
128
129                      ret += numseg[1] * (ss[i+1].h - ss[i].h);
130
131                      ret += abs(len[1] - last);
132
133                      last = len[1];
134
135               }
136
137               printf("%d\n",ret);
138
139        }
140
141        return 0;
142
143 }

  

   练习

hdu3265 Posters

hdu3642 Get The Treasury

poj2482 Stars in Your Window

poj2464 Brownie Points II

hdu3255 Farming

ural1707 Hypnotoad’s Secret

uva11983 Weird Advertisement

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

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

转载于:https://www.cnblogs.com/52dxer/p/10473499.html

【转载】完全版线段树 by notonlysuccess大牛相关推荐

  1. 牛客网 2018年全国多校算法寒假训练营练习比赛(第五场) H.Tree Recovery-完全版线段树(区间更新、区间求和)...

    H.Tree Recovery 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 131072K,其他语言262144K 64bit IO Format: %lld 链接:https:/ ...

  2. uscao 线段树成段更新操作及Lazy思想(POJ3468解题报告)

    线段树成段更新操作及Lazy思想(POJ3468解题报告) 标签: treequerybuildn2cstruct 2011-11-03 20:37 5756人阅读 评论(0) 收藏 举报  分类: ...

  3. 【DS】线段树HDU-1166

    /* * File: main.cpp * Author: lenovo * * Created on 2011年9月29日, 上午8:43*/ #include <cstdlib>#in ...

  4. 线段树简单入门 (含普通线段树, zkw线段树, 主席树)

    线段树简单入门 递归版线段树 线段树的定义 线段树, 顾名思义, 就是每个节点表示一个区间. 线段树通常维护一些区间的值, 例如区间和. 比如, 上图 \([2, 5]\) 区间的和, 为以下区间的和 ...

  5. NUIST OJ 1350-1352 面朝大海,春暖花开【初识线段树】

    NUIST OJ 1350-1352 面朝大海,春暖花开 NUIST OJ 1350-1352 面朝大海春暖花开 NUIST OJ 1350 面朝大海 春暖花开 基础版 NUIST OJ 1351 面 ...

  6. 转载自杭电大牛的博客 线段树 绝对经典

    转载自:http://www.notonlysuccess.com/ 不可不看的经典 学线段树必看,大牛很多,给后人留下记录的却没有几个,谢谢这位大牛~! 因为我这最近他博客打不开了...特意从别人那 ...

  7. 转载notonlysuccess的线段树博客

    介绍: 线段树和树状数组,都可以用来解决RMQ问题,树状数组实现起来较简单,简洁.但是能用树状数组解决的问题都可以用线段树做,而线段树能解决的问题,树状数组却未必能做. http://blog.csd ...

  8. 线段树模板(来自胡浩大牛)

    http://www.notonlysuccess.com/(今天看二叉树,想回来看看,发现大牛博客进不去...) 如果要学,就要好好学.我copy的,如有错,请看http://www.cnblogs ...

  9. 大牛整理的线段树集锦

     转载自:http://www.notonlysuccess.com/ 膜拜之... [完全版]线段树 很早前写的那篇线段树专辑至今一直是本博客阅读点击量最大的一片文章,当时觉得挺自豪的,还去pk ...

最新文章

  1. debian10 dhcp简单配置
  2. Inplayable技术分享
  3. 超大数据下大批量随机键值的查询优化方案
  4. [设计模式]简单工厂模式
  5. 【转载】笛卡尔转极坐标
  6. 2021年11月软考准考证打印时间及操作步骤
  7. 11.使用ForwardAction实现页面屏蔽。
  8. 加密 解密常用的算法
  9. 商汤 AI TECH DAY丨见大咖、拿OFFER、体验AI,一次就够!
  10. 请指点一下,讨论也可以,顶也有分
  11. 卡耐基大学计算机专业分类,卡内基梅隆大学计算机专业
  12. Flex实现分页显示功能(mx:DataGrid)
  13. Vue 配置请求本地Json数据
  14. 10天学会avr单片机和c语言,郭天祥十天学会AVR单片机
  15. 使用prewitt算子分割白纸黑字图像(Matlab)
  16. Google Driver 询问国内手机号怎么办,谷歌云盘下载配额不足
  17. 分散的无纸记录仪如何通过无线集中实现短信报警
  18. 学大伟业:如何利用课余时间学习物理竞赛,搞定自主招生?
  19. 主板上的内存插槽颜色到底代表了什么意思?
  20. python实现守护进程_Python如何实现守护进程的方法示例

热门文章

  1. 第三届江西省高校网络安全技能大赛 部分wpCrypto的疑惑
  2. 泰迪云课堂数据分析案例:广电大数据营销推荐项目
  3. 爱德华·琼斯(Edward Jones)公司
  4. 请将第4章例4-6中的问卷调查结果用文本文件result保存, 并编写程序读该文件然后统计各评语出现的次数,再将最终统计结果追加至esultxt文件中
  5. Web安全学习day01
  6. win10在几个窗口间切换的快捷键
  7. 盘点测试分析工具资源,文章教程/视频教程等你来体验!
  8. 基于规则的分形图形生成方法
  9. ubuntu 20.04 安装谷歌输入法
  10. wcdma系统随机接入过程的流程图_一种随机接入方法与流程