ACM大牛总结的线段树专辑,超经典的_第1页
ACM大牛总结的线段树专辑,超经典的_第2页
ACM大牛总结的线段树专辑,超经典的_第3页
ACM大牛总结的线段树专辑,超经典的_第4页
ACM大牛总结的线段树专辑,超经典的_第5页
已阅读5页,还剩17页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

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

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

3、得线段树的题目整体上可以分成以下四个部分:· 单点更新:最最基础的线段树,只更新叶子节点,然后把信息用PushUP(int r)这个函数更新上来o hdu1166 敌兵布阵题意:O(-1)思路:O(-1)线段树功能:update:单点增减 query:区间求和?View Code CPP12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758#include <cstdio> #define ls

4、on l , m , rt << 1#define rson m + 1 , r , rt << 1 | 1const int maxn = 55555;int summaxn<<2;void PushUP(int rt) sumrt = sumrt<<1 + sumrt<<1|1;void build(int l,int r,int rt) if (l = r) scanf("%d",&sumrt);return ;int m = (l + r) >> 1;build(lson);build(

5、rson);PushUP(rt);void update(int p,int add,int l,int r,int rt) if (l = r) sumrt += add;return ;int m = (l + r) >> 1;if (p <= m) update(p , add , lson);else update(p , add , rson);PushUP(rt);int query(int L,int R,int l,int r,int rt) if (L <= l && r <= R) return sumrt;int m = (l

6、 + r) >> 1;int ret = 0;if (L <= m) ret += query(L , R , lson);if (R > m) ret += query(L , R , rson);return ret;int main() int T , n;scanf("%d",&T);for (int cas = 1 ; cas <= T ; cas +) printf("Case %d:n",cas);scanf("%d",&n);build(1 , n , 1);char op

7、10;while (scanf("%s",op) if (op0 = 'E') break;int a , b;scanf("%d%d",&a,&b);if (op0 = 'Q') printf("%dn",query(a , b , 1 , n , 1);else if (op0 = 'S') update(a , -b , 1 , n , 1);else update(a , b , 1 , n , 1);return 0;o hdu1754 I Hate It题意:

8、O(-1)思路:O(-1)线段树功能:update:单点替换 query:区间最值?View Code CPP12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455#include <cstdio>#include <algorithm>using namespace std; #define lson l , m , rt << 1#define rson m + 1 , r , rt &l

9、t;< 1 | 1const int maxn = 222222;int MAXmaxn<<2;void PushUP(int rt) MAXrt = max(MAXrt<<1 , MAXrt<<1|1);void build(int l,int r,int rt) if (l = r) scanf("%d",&MAXrt);return ;int m = (l + r) >> 1;build(lson);build(rson);PushUP(rt);void update(int p,int sc,int l,

10、int r,int rt) if (l = r) MAXrt = sc;return ;int m = (l + r) >> 1;if (p <= m) update(p , sc , lson);else update(p , sc , rson);PushUP(rt);int query(int L,int R,int l,int r,int rt) if (L <= l && r <= R) return MAXrt;int m = (l + r) >> 1;int ret = 0;if (L <= m) ret = max

11、(ret , query(L , R , lson);if (R > m) ret = max(ret , query(L , R , rson);return ret;int main() int n , m;while (scanf("%d%d",&n,&m) build(1 , n , 1);while (m -) char op2;int a , b;scanf("%s%d%d",op,&a,&b);if (op0 = 'Q') printf("%dn",query(a ,

12、 b , 1 , n , 1);else update(a , b , 1 , n , 1);return 0;o hdu1394 Minimum Inversion Number题意:求Inversion后的最小逆序数思路:用O(nlogn)复杂度求出最初逆序数后,就可以用O(1)的复杂度分别递推出其他解线段树功能:update:单点增减 query:区间求和?View Code CPP123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354

13、55565758#include <cstdio>#include <algorithm>using namespace std; #define lson l , m , rt << 1#define rson m + 1 , r , rt << 1 | 1const int maxn = 5555;int summaxn<<2;void PushUP(int rt) sumrt = sumrt<<1 + sumrt<<1|1;void build(int l,int r,int rt) sumrt

14、 = 0;if (l = r) return ;int m = (l + r) >> 1;build(lson);build(rson);void update(int p,int l,int r,int rt) if (l = r) sumrt +;return ;int m = (l + r) >> 1;if (p <= m) update(p , lson);else update(p , rson);PushUP(rt);int query(int L,int R,int l,int r,int rt) if (L <= l && r

15、 <= R) return sumrt;int m = (l + r) >> 1;int ret = 0;if (L <= m) ret += query(L , R , lson);if (R > m) ret += query(L , R , rson);return ret;int xmaxn;int main() int n;while (scanf("%d",&n) build(0 , n - 1 , 1);int sum = 0;for (int i = 0 ; i < n ; i +) scanf("%d&

16、quot;,&xi);sum += query(xi , n - 1 , 0 , n - 1 , 1);update(xi , 0 , n - 1 , 1);int ret = sum;for (int i = 0 ; i < n ; i +) sum += n - xi - xi - 1;ret = min(ret , sum);printf("%dn",ret);return 0;o hdu2795 Billboard题意:h*w的木板,放进一些1*L的物品,求每次放空间能容纳且最上边的位子思路:每次找到最大值的位子,然后减去L线段树功能:query:区间

17、求最大值的位子(直接把update的操作在query里做了)?View Code CPP123456789101112131415161718192021222324252627282930313233343536373839404142#include <cstdio>#include <algorithm>using namespace std; #define lson l , m , rt << 1#define rson m + 1 , r , rt << 1 | 1const int maxn = 222222;i

18、nt h , w , n;int MAXmaxn<<2;void PushUP(int rt) MAXrt = max(MAXrt<<1 , MAXrt<<1|1);void build(int l,int r,int rt) MAXrt = w;if (l = r) return ;int m = (l + r) >> 1;build(lson);build(rson);int query(int x,int l,int r,int rt) if (l = r) MAXrt -= x;return l;int m = (l + r) >&

19、gt; 1;int ret = (MAXrt<<1 >= x) ? query(x , lson) : query(x , rson);PushUP(rt);return ret;int main() while (scanf("%d%d%d",&h,&w,&n) if (h > n) h = n;build(1 , h , 1);while (n -) int x;scanf("%d",&x);if (MAX1 < x) puts("-1");else printf(&q

20、uot;%dn",query(x , 1 , h , 1);return 0;· 练习:o poj2828 Buy Ticketso poj2886 Who Gets the Most Candies?· 成段更新(通常这对初学者来说是一道坎),需要用到延迟标记(或者说懒惰标记),简单来说就是每次更新的时候不要更新到底,用延迟标记使得更新延迟到下次需要更新or询问到的时候o hdu1698 Just a Hook题意:O(-1)思路:O(-1)线段树功能:update:成段替换 (由于只query一次总区间,所以可以直接输出1结点的信息)?View Code

21、60;CPP123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657#include <cstdio>#include <algorithm>using namespace std; #define lson l , m , rt << 1#define rson m + 1 , r , rt << 1 | 1const int maxn = 111111;int h , w , n;

22、int colmaxn<<2;int summaxn<<2;void PushUp(int rt) sumrt = sumrt<<1 + sumrt<<1|1;void PushDown(int rt,int m) if (colrt) colrt<<1 = colrt<<1|1 = colrt;sumrt<<1 = (m - (m >> 1) * colrt;sumrt<<1|1 = (m >> 1) * colrt;colrt = 0;void build(int l,i

23、nt r,int rt) colrt = 0;sumrt = 1;if (l = r) return ;int m = (l + r) >> 1;build(lson);build(rson);PushUp(rt);void update(int L,int R,int c,int l,int r,int rt) if (L <= l && r <= R) colrt = c;sumrt = c * (r - l + 1);return ;PushDown(rt , r - l + 1);int m = (l + r) >> 1;if (L

24、<= m) update(L , R , c , lson);if (R > m) update(L , R , c , rson);PushUp(rt);int main() int T , n , m;scanf("%d",&T);for (int cas = 1 ; cas <= T ; cas +) scanf("%d%d",&n,&m);build(1 , n , 1);while (m -) int a , b , c;scanf("%d%d%d",&a,&b,&a

25、mp;c);update(a , b , c , 1 , n , 1);printf("Case %d: The total value of the hook is %d.n",cas , sum1);return 0;o poj3468 A Simple Problem with Integers题意:O(-1)思路:O(-1)线段树功能:update:成段增减 query:区间求和?View Code CPP1234567891011121314151617181920212223242526272829303132333435363738394041424

26、344454647484950515253545556575859606162636465666768697071727374#include <cstdio>#include <algorithm>using namespace std; #define lson l , m , rt << 1#define rson m + 1 , r , rt << 1 | 1#define LL long longconst int maxn = 111111;LL addmaxn<<2;LL summaxn<<2;vo

27、id PushUp(int rt) sumrt = sumrt<<1 + sumrt<<1|1;void PushDown(int rt,int m) if (addrt) addrt<<1 += addrt;addrt<<1|1 += addrt;sumrt<<1 += addrt * (m - (m >> 1);sumrt<<1|1 += addrt * (m >> 1);addrt = 0;void build(int l,int r,int rt) addrt = 0;if (l = r)

28、scanf("%lld",&sumrt);return ;int m = (l + r) >> 1;build(lson);build(rson);PushUp(rt);void update(int L,int R,int c,int l,int r,int rt) if (L <= l && r <= R) addrt += c;sumrt += (LL)c * (r - l + 1);return ;PushDown(rt , r - l + 1);int m = (l + r) >> 1;if (L <

29、;= m) update(L , R , c , lson);if (m < R) update(L , R , c , rson);PushUp(rt);LL query(int L,int R,int l,int r,int rt) if (L <= l && r <= R) return sumrt;PushDown(rt , r - l + 1);int m = (l + r) >> 1;LL ret = 0;if (L <= m) ret += query(L , R , lson);if (m < R) ret += que

30、ry(L , R , rson);return ret;int main() int N , Q;scanf("%d%d",&N,&Q);build(1 , N , 1);while (Q -) char op2;int a , b , c;scanf("%s",op);if (op0 = 'Q') scanf("%d%d",&a,&b);printf("%lldn",query(a , b , 1 , N , 1); else scanf("%d%d%d&

31、quot;,&a,&b,&c);update(a , b , c , 1 , N , 1);return 0;o poj2528 Mayors posters题意:在墙上贴海报,海报可以互相覆盖,问最后可以看见几张海报思路:这题数据范围很大,直接搞超时+超内存,需要离散化:离散化简单的来说就是只取我们需要的值来用,比如说区间1000,2000,1990,2012 我们用不到-,9991001,19891991,19992001,20112013,+这些值,所以我只需要1000,1990,2000,2012就够了,将其分别映射到0,1,2,3,在于复杂度就大大的降下来了所

32、以离散化要保存所有需要用到的值,排序后,分别映射到1n,这样复杂度就会小很多很多而这题的难点在于每个数字其实表示的是一个单位长度(并且一个点),这样普通的离散化会造成许多错误(包括我以前的代码,poj这题数据奇弱)给出下面两个简单的例子应该能体现普通离散化的缺陷:1-10 1-4 5-101-10 1-4 6-10为了解决这种缺陷,我们可以在排序后的数组上加些处理,比如说1,2,6,10如果相邻数字间距大于1的话,在其中加上任意一个数字,比如加成1,2,3,6,7,10,然后再做线段树就好了.线段树功能:update:成段替换 query:简单hash?View Code CPP12

33、3456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384#include <cstdio>#include <cstring>#include <algorithm>using namespace std;#define lson l , m , rt << 1#define rson m +

34、 1 , r , rt << 1 | 1 const int maxn = 11111;bool hashmaxn;int limaxn , rimaxn;int Xmaxn*3;int colmaxn<<4;int cnt; void PushDown(int rt) if (colrt != -1) colrt<<1 = colrt<<1|1 = colrt;colrt = -1;void update(int L,int R,int c,int l,int r,int rt) if (L <= l &&am

35、p; r <= R) colrt = c;return ;PushDown(rt);int m = (l + r) >> 1;if (L <= m) update(L , R , c , lson);if (m < R) update(L , R , c , rson);void query(int l,int r,int rt) if (colrt != -1) if (!hashcolrt) cnt +;hash colrt = true;return ;if (l = r) return ;int m = (l + r) >> 1;query(l

36、son);query(rson);int Bin(int key,int n,int X) int l = 0 , r = n - 1;while (l <= r) int m = (l + r) >> 1;if (Xm = key) return m;if (Xm < key) l = m + 1;else r = m - 1;return -1;int main() int T , n;scanf("%d",&T);while (T -) scanf("%d",&n);int nn = 0;for (int i

37、 = 0 ; i < n ; i +) scanf("%d%d",&lii , &rii);Xnn+ = lii;Xnn+ = rii;sort(X , X + nn);int m = 1;for (int i = 1 ; i < nn; i +) if (Xi != Xi-1) Xm + = Xi;for (int i = m - 1 ; i > 0 ; i -) if (Xi != Xi-1 + 1) Xm + = Xi-1 + 1;sort(X , X + m);memset(col , -1 , sizeof(col);for (i

38、nt i = 0 ; i < n ; i +) int l = Bin(lii , m , X);int r = Bin(rii , m , X);update(l , r , i , 0 , m , 1);cnt = 0;memset(hash , false , sizeof(hash);query(0 , m , 1);printf("%dn",cnt);return 0;o poj3225 Help with Intervals题意:区间操作,交,并,补等思路:我们一个一个操作来分析:(用0和1表示是否包含区间,-1表示该区间内既有包含又有不包含)U:把区间l

39、,r覆盖成1I:把-,l)(r,覆盖成0D:把区间l,r覆盖成0C:把-,l)(r,覆盖成0 , 且l,r区间0/1互换S:l,r区间0/1互换成段覆盖的操作很简单,比较特殊的就是区间0/1互换这个操作,我们可以称之为异或操作很明显我们可以知道这个性质:当一个区间被覆盖后,不管之前有没有异或标记都没有意义了所以当一个节点得到覆盖标记时把异或标记清空而当一个节点得到异或标记的时候,先判断覆盖标记,如果是0或1,直接改变一下覆盖标记,不然的话改变异或标记开区间闭区间只要数字乘以2就可以处理(偶数表示端点,奇数表示两端点间的区间)线段树功能:update:成段替换,区间异或 query:简单hash

40、?View Code CPP123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899#include <cstdio>#include <cstring>#include <cctype>#include <algorithm

41、>using namespace std;#define lson l , m , rt << 1#define rson m + 1 , r , rt << 1 | 1 const int maxn = 131072;bool hashmaxn;int covermaxn<<2;int XORmaxn<<2;void FXOR(int rt) if (coverrt != -1) coverrt = 1;else XORrt = 1;void PushDown(int rt) if (coverrt != -1) coverrt

42、<<1 = coverrt<<1|1 = coverrt;XORrt<<1 = XORrt<<1|1 = 0;coverrt = -1;if (XORrt) FXOR(rt<<1);FXOR(rt<<1|1);XORrt = 0;void update(char op,int L,int R,int l,int r,int rt) if (L <= l && r <= R) if (op = 'U') coverrt = 1;XORrt = 0; else if (op = &#

43、39;D') coverrt = 0;XORrt = 0; else if (op = 'C' | op = 'S') FXOR(rt);return ;PushDown(rt);int m = (l + r) >> 1;if (L <= m) update(op , L , R , lson);else if (op = 'I' | op = 'C') XORrt<<1 = coverrt<<1 = 0;if (m < R) update(op , L , R , rson

44、);else if (op = 'I' | op = 'C') XORrt<<1|1 = coverrt<<1|1 = 0;void query(int l,int r,int rt) if (coverrt = 1) for (int it = l ; it <= r ; it +) hashit = true;return ; else if (coverrt = 0) return ;if (l = r) return ;PushDown(rt);int m = (l + r) >> 1;query(lson);q

45、uery(rson);int main() cover1 = XOR1 = 0;char op , l , r;int a , b;while ( scanf("%c %c%d,%d%cn",&op , &l , &a , &b , &r) ) a <<= 1 , b <<= 1;if (l = '(') a +;if (r = ')') b -;if (a > b) if (op = 'C' | op = 'I') cover1 = XOR

46、1 = 0; else update(op , a , b , 0 , maxn , 1);query(0 , maxn , 1);bool flag = false;int s = -1 , e;for (int i = 0 ; i <= maxn ; i +) if (hashi) if (s = -1) s = i;e = i; else if (s != -1) if (flag) printf(" ");flag = true;printf("%c%d,%d%c",s&1?'(':'' , s>

47、;>1 , (e+1)>>1 , e&1?')':'');s = -1;if (!flag) printf("empty set");puts("");return 0;· 练习:o poj1436 Horizontally Visible Segmentso poj2991 Craneo Another LCISo Bracket Sequence· 区间合并这类题目会询问区间中满足条件的连续最长区间,所以PushUp的时候需要对左右儿子的区间进行合并o poj3667 Ho

48、tel题意:1 a:询问是不是有连续长度为a的空房间,有的话住进最左边2 a b:将a,a+b-1的房间清空思路:记录区间中最长的空房间线段树操作:update:区间替换 query:询问满足条件的最左断点?View Code CPP1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677#include <cstdio>#include &

49、lt;cstring>#include <cctype>#include <algorithm>using namespace std;#define lson l , m , rt << 1#define rson m + 1 , r , rt << 1 | 1 const int maxn = 55555;int lsummaxn<<2 , rsummaxn<<2 , msummaxn<<2;int covermaxn<<2; void PushDown(int rt,

50、int m) if (coverrt != -1) coverrt<<1 = coverrt<<1|1 = coverrt;msumrt<<1 = lsumrt<<1 = rsumrt<<1 = coverrt ? 0 : m - (m >> 1);msumrt<<1|1 = lsumrt<<1|1 = rsumrt<<1|1 = coverrt ? 0 : (m >> 1);coverrt = -1;void PushUp(int rt,int m) lsumrt = ls

51、umrt<<1;rsumrt = rsumrt<<1|1;if (lsumrt = m - (m >> 1) lsumrt += lsumrt<<1|1;if (rsumrt = (m >> 1) rsumrt += rsumrt<<1;msumrt = max(lsumrt<<1|1 + rsumrt<<1 , max(msumrt<<1 , msumrt<<1|1);void build(int l,int r,int rt) msumrt = lsumrt = rsum

52、rt = r - l + 1;coverrt = -1;if (l = r) return ;int m = (l + r) >> 1;build(lson);build(rson);void update(int L,int R,int c,int l,int r,int rt) if (L <= l && r <= R) msumrt = lsumrt = rsumrt = c ? 0 : r - l + 1;coverrt = c;return ;PushDown(rt , r - l + 1);int m = (l + r) >> 1

53、;if (L <= m) update(L , R , c , lson);if (m < R) update(L , R , c , rson);PushUp(rt , r - l + 1);int query(int w,int l,int r,int rt) if (l = r) return l;PushDown(rt , r - l + 1);int m = (l + r) >> 1;if (msumrt<<1 >= w) return query(w , lson);else if (rsumrt<<1 + lsumrt<<1|1 >= w) return m - rsumrt<<1 + 1;return query(w , rson);int main() int n , m;scanf("%d%d",&n,&m);bui

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论