线段树专题(不定期更新)
Posted 萌萌的美男子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树专题(不定期更新)相关的知识,希望对你有一定的参考价值。
1、hdu 1166 敌兵布阵(★☆☆☆☆)
题意:有n个营地,每个营地初始各有若干人,每次询问[l,r]营地的总人数,或者对某个营地加上或减去若干人数。
思路:线段树单点更新,区间查询
1 //线段树单点更新,区间查询 2 #include<iostream> 3 using namespace std; 4 const int maxn = 50010; 5 int N; 6 int barracks[maxn]; 7 int Tree[4 * maxn]; 8 void Create(int root,int l,int r) 9 { 10 if (l == r) 11 { 12 Tree[root] = barracks[l]; 13 return; 14 } 15 int mid = (l + r) / 2; 16 Create(root * 2 + 1, l, mid); 17 Create(root * 2 + 2, mid + 1, r); 18 Tree[root] = Tree[root * 2 + 1] + Tree[root * 2 + 2]; 19 } 20 void Update(int root, int l, int r, int pos, int v) 21 {//在第i个营地加上v个人 22 if (l > pos || r < pos) return; 23 else if (l == r&&l == pos) 24 { 25 Tree[root] += v; 26 return; 27 } 28 else 29 { 30 int mid = (l + r) / 2; 31 Update(root * 2 + 1, l, mid, pos, v); 32 Update(root * 2 + 2, mid+1, r, pos, v); 33 Tree[root] = Tree[root * 2 + 1] + Tree[root * 2 + 2]; 34 } 35 } 36 int Query(int root, int l, int r, int ll, int rr) 37 {//查询区间ll~rr的总人数 38 if (l > rr || r < ll) return 0; 39 else if (r <= rr&&l >= ll) return Tree[root]; 40 else 41 { 42 int mid = (l + r) / 2; 43 return Query(root * 2 + 1, l, mid, ll, rr) + Query(root * 2 + 2, mid + 1, r, ll, rr); 44 } 45 } 46 int main() 47 { 48 int T; 49 int k = 1; 50 scanf("%d", &T); 51 while (T--) 52 { 53 memset(Tree, 0, sizeof(Tree)); 54 scanf("%d", &N); 55 for (int i = 0; i < N; i++) scanf("%d",&barracks[i]); 56 Create(0, 0, N - 1); 57 char op[10]; 58 printf("Case %d:\n", k); 59 while (scanf("%s", op)) 60 { 61 if (op[0] == ‘E‘) 62 { 63 break; 64 } 65 else if (op[0] == ‘A‘) 66 { 67 int i, j; 68 scanf("%d%d", &i, &j); 69 Update(0, 0, N - 1, i - 1, j); 70 } 71 else if (op[0] == ‘S‘) 72 { 73 int i, j; 74 scanf("%d%d", &i, &j); 75 j = -j; 76 Update(0, 0, N - 1, i - 1, j); 77 } 78 else 79 { 80 int i, j; 81 scanf("%d%d", &i, &j); 82 printf("%d\n", Query(0, 0, N - 1, i-1, j-1)); 83 } 84 } 85 k++; 86 } 87 return 0; 88 }
2、hdu 1754 I Hate It(★☆☆☆☆)
题意:有n个学生,每次查询[l,r]之间学生的最高分,或者修改某个学生的成绩
思路:线段树单点更新,区间查询
1 //线段树-区间最值,单点更新,区间查询 2 #include<iostream> 3 #include<memory.h> 4 #include<algorithm> 5 using namespace std; 6 int n, m; 7 const int maxn = 200010; 8 int Score[maxn]; 9 int Tree[4 * maxn]; 10 void Create(int root, int l, int r) 11 { 12 if (l == r) 13 { 14 Tree[root] = Score[l]; 15 return; 16 } 17 int mid = (l + r) / 2; 18 Create(root * 2 + 1, l, mid); 19 Create(root * 2 + 2, mid + 1, r); 20 Tree[root] = max(Tree[root * 2 + 1], Tree[root * 2 + 2]); 21 } 22 void Update(int root, int l, int r, int pos, int v) 23 { 24 if (l > pos || r < pos) return; 25 else if (l == r&&l == pos) 26 { 27 Tree[root] = v; 28 return; 29 } 30 else 31 { 32 int mid = (l + r) / 2; 33 Update(root * 2 + 1, l, mid, pos, v); 34 Update(root * 2 + 2, mid + 1, r, pos, v); 35 Tree[root] = max(Tree[root * 2 + 1], Tree[root * 2 + 2]); 36 } 37 } 38 int Query(int root, int l, int r, int ll, int rr) 39 { 40 if (l > rr || r < ll) return 0; 41 else if (l >= ll&&r <= rr) return Tree[root]; 42 else 43 { 44 int mid = (l + r) / 2; 45 return max(Query(root * 2 + 1, l, mid, ll, rr), Query(root * 2 + 2, mid + 1, r, ll, rr)); 46 } 47 48 } 49 int main() 50 { 51 while (~scanf("%d%d", &n, &m)) 52 { 53 memset(Tree, 0, sizeof(Tree)); 54 for (int i = 0; i < n; i++) scanf("%d",&Score[i]); 55 char c[5]; 56 Create(0, 0, n - 1); 57 for (int i = 0; i < m; i++) 58 { 59 scanf("%s",c); 60 switch (c[0]) 61 { 62 case ‘Q‘: 63 int ll, rr; 64 scanf("%d%d", &ll, &rr); 65 printf("%d\n",Query(0,0,n - 1,ll-1,rr-1)); 66 break; 67 case ‘U‘: 68 int id, v; 69 scanf("%d%d", &id, &v); 70 Update(0, 0, n - 1, id - 1, v); 71 break; 72 } 73 } 74 } 75 return 0; 76 }
3、hdu1394 Minimum Inversion Number(★★★☆☆)
题意:给出0~n-1这些数的一个排列,然后每次可以把首个放在末尾,共n个排列。求这些排列中逆序对的最小值
思路:线段树单点更新,区间查询。Tree[root]是指在[l,r]区间内比l大,小于等于r的个数。每次先询问[num[i],n-1]的数目,即求当前比num[i]大的数的个数,再放入num[i],更新。之后,将num[i]移到后面,比num[i]大的有n - 1 - num[i]个,比num[i]小的有num[i]个,移到最后,新增n - 1 - num[i]个逆序对,减少num[i]个逆序对。
1 #include<iostream> 2 #include<memory.h> 3 #include<algorithm> 4 using namespace std; 5 const int maxn = 5010; 6 int num[maxn]; 7 int Tree[maxn * 4];//Tree[root]是指在[l,r]区间内比l大,小于等于r的个数 8 int n; 9 void Update(int root, int l, int r, int pos) 10 { 11 if (l > pos || r < pos)return; 12 else if (l == r&&l == pos) 13 { 14 Tree[root]++; 15 return; 16 } 17 else 18 { 19 int mid = (l + r) / 2; 20 Update(root * 2 + 1, l, mid, pos); 21 Update(root * 2 + 2, mid + 1, r, pos); 22 Tree[root] = Tree[root * 2 + 1] + Tree[root * 2 + 2]; 23 } 24 } 25 int Query(int root, int l, int r, int ll, int rr) 26 { 27 if (r<ll || l>rr) return 0; 28 else if (l >= ll&&r <= rr) return Tree[root]; 29 else 30 { 31 int mid = (l + r) / 2; 32 return Query(root * 2 + 1, l, mid, ll, rr) + Query(root * 2 + 2, mid + 1, r, ll, rr); 33 } 34 } 35 int main() 36 { 37 while (~scanf("%d", &n)) 38 { 39 memset(Tree, 0, sizeof(Tree)); 40 int Sum = 0; 41 for (int i = 0; i < n; i++) 42 { 43 scanf("%d", &num[i]); 44 Sum += Query(0, 0, n - 1, num[i], n - 1);//先询问[num[i],n-1]的数目,即求当前比num[i]大的数的个数 45 Update(0, 0, n - 1, num[i]);//再放入num[i] 46 } 47 int ans = Sum; 48 for (int i = 0; i < n - 1; i++) 49 { 50 Sum += n - 1 - num[i] - num[i];//将num[i]移到后面,比num[i]大的有n - 1 - num[i]个,比num[i]小的有num[i]个,移到最后,新增n - 1 - num[i]个逆序对,减少num[i]个逆序对 51 ans = min(ans, Sum); 52 } 53 printf("%d\n", ans); 54 } 55 return 0; 56 }
4、hdu 2795 Billboard(★★★☆☆)
题意:有一块h*w的告示板,有若干个单位高度、宽度为wi的告示,按输入顺序每次尽可能向最上方、最靠左摆放,相互之间不能重叠。求出每张告示的行数,若不存在,则输出-1.
思路:线段树单点更新,区间查询。Tree[root]表示第l行到第r行每行最大剩余可放置的最大值,初始设为w,大小为min(h,n)*4.模拟在线处理,每次输入一张告示的宽度wi,如果Tree[0]<wi,说明所有的行都放不下,否则,如果Tree[mid]大于等于wi,则往左走,否则再往右走,这样就能满足每次都尽可能向上、向左放。之后不断重复,直到L==R,说明该行是能够放下的最上一行,然后Tree[root]-=wi,进行更新。这道题把询问和更新合并起来。
1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 int h, w, n; 5 const int maxn = 200010; 6 int Tree[maxn * 4]; 7 void Create(int root, int l, int r) 8 { 9 if (l == r) 10 { 11 Tree[root] = w; 12 return; 13 } 14 int mid = (l + r) / 2; 15 Create(root * 2 + 1, l, mid); 16 Create(root * 2 + 2, mid + 1, r); 17 Tree[root] = max(Tree[root * 2 + 1], Tree[root * 2 + 2]); 18 } 19 int Query(int root, int l, int r, int v) 20 { 21 if (l == r) 22 { 23 Tree[root] -= v; 24 return l+1; 25 } 26 else 27 { 28 int mid = (l + r) / 2; 29 int ans; 30 if (Tree[root * 2 + 1] >= v) ans=Query(root * 2 + 1, l, mid, v); 31 else ans=Query(root * 2 + 2, mid + 1, r, v); 32 Tree[root] = max(Tree[root * 2 + 1], Tree[root * 2 + 2]); 33 return ans; 34 } 35 } 36 int main() 37 { 38 while (~scanf("%d%d%d", &h, &w, &n)) 39 { 40 memset(Tree, 0, sizeof(Tree)); 41 if (h > n) h = n; 42 Create(0,0,h - 1); 43 for (int i = 0; i < n; i++) 44 { 45 int len; 46 scanf("%d", &len); 47 if (Tree[0] < len) printf("-1\n"); 48 else printf("%d\n", Query(0, 0, h - 1, len)); 49 } 50 } 51 return 0; 52 }
5、hdu 1698 Just a Hook(★★☆☆☆)
题意:有一个钩子,给出其长度。每次能把其中一段变为某种金属,不同金属价值不同;对于每次询问,输出区间内的总价值。
思路:线段树区间更新,区间查询。Tree[root]表示L到R的总价值。PushDown为延迟更新,此处Tree[root].d记为标记,即每次更新时不更新到底,每次区间更新或区间查询时再进行更新。
1 #include<iostream> 2 #include<memory.h> 3 using namespace std; 4 const int maxn=100010; 5 struct Node 6 { 7 int sum, d; 8 Node(int sum0=0,int d0=0):sum(sum0),d(d0){ } 9 }; 10 Node Tree[maxn * 4]; 11 int n,q; 12 void PushDown(int root,int l,int r) 13 { 14 if (Tree[root].d) 15 { 16 int mid = (r + l) / 2; 17 Tree[root * 2 + 1].d = Tree[root * 2 + 2].d = Tree[root].d; 18 Tree[root * 2 + 1].sum = Tree[root].d*(mid-l+ 1); 19 Tree[root*2+2].sum= Tree[root].d*(r-mid); 20 } 21 Tree[root].d = 0; 22 } 23 void Create(int root,int l,int r) 24 { 25 if (l == r) 26 { 27 Tree[root].sum = 1; 28 return; 29 } 30 int mid = (l + r) / 2; 31 Create(root * 2 + 1, l, mid); 32 Create(root * 2 + 2, mid + 1, r); 33 Tree[root].sum = Tree[root * 2 + 1].sum + Tree[root * 2 + 2].sum; 34 } 35 void Update(int root, int l, int r, int ll, int rr, int v) 36 { 37 if (r < ll || l>rr)return; 38 else if (l >= ll&&r <= rr) 39 { 40 Tree[root].sum = v*(r - l + 1); 41 Tree[root].d = v; 42 return; 43 } 44 PushDown(root,l,r); 45 int mid = (l + r) / 2; 46 Update(root*2+1, l, mid, ll, rr, v); 47 Update(root*2+2, mid + 1, r, ll, rr, v); 48 Tree[root].sum = Tree[root * 2 + 1].sum + Tree[root * 2 + 2].sum; 49 } 50 int Query(int root, int l, int r, int ll, int rr) 51 { 52 PushDown(root,l,r); 53 if (r< ll || l>rr) return 0; 54 else if (l >= ll&&r <= rr) return Tree[root].sum; 55 else 56 { 57 int mid = (l + r) / 2; 58 return Query(root * 2 + 1, l, mid, ll, rr) + Query(root * 2 + 2, mid + 1, r, ll, rr); 59 } 60 } 61 int main() 62 { 63 int t; 64 int k = 1; 65 scanf("%d", &t); 66 while (t--) 67 { 68 memset(Tree, 0, sizeof(Tree)); 69 scanf("%d%d", &n, &q); 70 Create(0, 0, n - 1); 71 for (int i = 0; i < q; i++) 72 { 73 int l,r, v; 74 scanf("%d%d%d", &l, &r, &v); 75 Update(0, 0, n - 1, l-1,r-1, v); 76 } 77 printf("Case %d: The total value of the hook is %d.\n", k,Query(0,0,n-1,0,n-1)); 78 k++; 79 } 80 return 0; 81 }
6、poj3468 A Simple Problem with Integers(★★☆☆☆)
题意:给出n个数,每次可以选取一段区间将区间内的数字加上或减去一个值,也可以选择查询一段区间内的数字之和。
思路:线段树区间更新,区间查询。
1 #include<iostream> 2 #include<memory.h> 3 using namespace std; 4 typedef long long LL; 5 const int maxn = 200100; 6 struct Node 7 { 8 LL sum; 9 LL d; 10 Node(LL s=0,LL d0=0):sum(s),d(d0){ } 11 }Tree[maxn*4]; 12 int n, q; 13 void PushDown(int root, int l, int r) 14 { 15 if (Tree[root].d) 16 { 17 int mid = (l + r) / 2; 18 Tree[root * 2 + 1].d += Tree[root].d; 19 Tree[root * 2 + 2].d += Tree[root].d; 20 Tree[root * 2 + 1].sum += 1ll*Tree[root].d*(mid - l + 1); 21 Tree[root * 2 + 2].sum += 1ll*Tree[root].d*(r - mid); 22 } 23 Tree[root].d = 0; 24 } 25 void PushUp(int root) 26 { 27 Tree[root].sum = Tree[root * 2 + 1].sum + Tree[root * 2 + 2].sum; 28 } 29 void Create(int root, int l, int r) 30 { 31 if (l == r) 32 { 33 scanf("%lld", &Tree[root].sum); 34 return; 35 } 36 int mid = (l + r) / 2; 37 Create(root * 2 + 1, l, mid); 38 Create(root * 2 + 2, mid + 1, r); 39 PushUp(root); 40 } 41 42 void Update(int root, int l, int r, int ll, int rr, int v) 43 { 44 if (l > rr || r < ll) return; 45 else if (l >= ll&&r <= rr) 46 { 47 Tree[root].sum += 1ll*v*(r - l + 1); 48 Tree[root].d += v; 49 return; 50 } 51 PushDown(root, l, r); 52 int mid = (l + r) / 2; 53 Update(root * 2 + 1, l, mid, ll, rr, v); 54 Update(root * 2 + 2, mid + 1, r, ll, rr, v); 55 PushUp(root); 56 } 57 LL Query(int root, int l, int r, int ll, int rr) 58 { 59 PushDown(root, l, r); 60 if (l > rr || r < ll) return 0; 61 else if (l >= ll&&r <= rr) 62 { 63 return Tree[root].sum; 64 } 65 else 66 { 67 int mid = (l + r) / 2; 68 LL ans = Query(root * 2 + 1, l, mid, ll, rr) + Query(root * 2 + 2, mid + 1, r, ll, rr); 69 PushUp(root); 70 return ans; 71 } 72 } 73 int main() 74 { 75 while (~scanf("%d%d", &n, &q)) 76 { 77 memset(Tree, 0, sizeof(Tree)); 78 Create(0, 0, n - 1); 79 char c[2]; 80 for (int i = 0; i < q; i++) 81 { 82 scanf("%s", c); 83 if (c[0] == ‘Q‘) 84 { 85 int ll, rr; 86 scanf("%d%d", &ll, &rr); 87 printf("%lld\n", Query(0, 0, n - 1, ll - 1, rr - 1)); 88 } 89 else 90 { 91 int ll, rr, v; 92 scanf("%d%d%d", &ll, &rr, &v); 93 Update(0, 0, n - 1, ll-1, rr-1, v); 94 } 95 } 96 } 97 return 0; 98 }
7、poj2528 Mayor’s posters(★★★★☆)
题意:给出n张海报的区间,后来的海报能覆盖掉原来的海报,求最后能看到的海报的数目。
思路:1、由于长度的范围很大,所以需要离散化,把所有海报的左右位置排个序(对于不相邻的两个位置,需要插入一个介于两者之间的位置,防止离散化后覆盖出错);2、Tree[root]表示在[l,r]长度区间内所覆盖的海报序号。
1 #include<iostream> 2 #include<memory.h> 3 #include<algorithm> 4 using namespace std; 5 int n, m; 6 const int maxn = 11111; 7 int Tree[maxn*2* 4*2]; 8 struct Num 9 { 10 int l; 11 int r; 12 Num(int ll = 0, int rr = 0) :l(ll), r(rr) 13 { 14 } 15 }num[maxn]; 16 bool vis[maxn]; 17 int Hush[maxn*4]; 18 int cnt = 0; 19 int PosHush(int v) 20 { 21 int l = 0, r = m - 1; 22 while (l <= r) 23 { 24 int mid = (l + r) / 2; 25 if (Hush[mid] == v) return mid; 26 else if (Hush[mid] > v) r = mid - 1; 27 else l = mid + 1; 28 } 29 return -1; 30 } 31 void PushDown(int root, int l, int r) 32 { 33 if (l == r) return; 34 if (Tree[root]) 35 { 36 Tree[root * 2 + 1] = Tree[root * 2 + 2] = Tree[root]; 37 Tree[root] = 0; 38 } 39 } 40 void Update(int root, int l, int r, int ll, int rr, int v) 41 { 42 PushDown(root, l, r); 43 if (l > rr || r < ll) return; 44 else if (ll <= l&&r <= rr) 45 { 46 Tree[root] = v; 47 return; 48 } 49 int mid = (l + r) / 2; 50 Update(root * 2 + 1, l, mid, ll, rr, v); 51 Update(root * 2 + 2, mid + 1, r, ll, rr, v); 52 } 53 54 void Query(int root, int l, int r) 55 { 56 PushDown(root, l, r); 57 if (Tree[root]) 58 { 59 if (!vis[Tree[root]]) cnt++; 60 vis[Tree[root]] = true; 61 return; 62 } 63 if (l == r) return; 64 int mid = (l + r) / 2; 65 Query(root * 2 + 1, l, mid); 66 Query(root * 2 + 2, mid + 1, r); 67 } 68 int main() 69 { 70 int t; 71 scanf("%d", &t); 72 while (t--) 73 { 74 scanf("%d", &n); 75 m = 0; 76 for (int i = 0; i < n; i++) 77 { 78 scanf("%d%d", &num[i].l, &num[i].r); 79 Hush[m++] = num[i].l; 80 Hush[m++] = num[i].r; 81 } 82 sort(Hush, Hush + m); 83 int m2 = 1; 84 for (int i = 1; i < m; i++) 85 { 86 if (Hush[i] != Hush[i - 1]) Hush[m2++] = Hush[i]; 87 } 88 m = m2; 89 for (int i = 1; i < m2; i++) 90 { 91 if (Hush[i] != Hush[i - 1] + 1) Hush[m++] = Hush[i - 1] + 1; 92 } 93 sort(Hush, Hush + m); 94 memset(Tree, 0, sizeof(Tree)); 95 for (int i = 0; i < n; i++) 96 { 97 int ll = PosHush(num[i].l); 98 int rr = PosHush(num[i].r); 99 Update(0, 0, m - 1, ll, rr, i + 1); 100 } 101 memset(vis, 0, sizeof(vis)); 102 cnt = 0; 103 Query(0, 0, m - 1); 104 printf("%d\n", cnt); 105 } 106 return 0; 107 }
8、POJ3225 Help with Intervals(★★★★☆)
题意:原来有一个空集合,每次进行集合的并、交、对称差、差,最后按升序输出集合中的连续区间,若为空则输出set empty.
思路:1、区间的开闭:倍增,如果左开l++,如果右开r--;
2、运算的处理:
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互换
3、如何更新:
①当一个节点得到覆盖标记时把异或标记清空;
②而当一个节点得到异或标记的时候,先判断覆盖标记,如果是0或1,直接改变一下覆盖标记,不然的话改变异或标记。
1 #include <cstdio> 2 #include <iostream> 3 #include<memory.h> 4 using namespace std; 5 const int M = 70000 * 2; 6 struct segment 7 { 8 int tag; //1表示区间所有被覆盖,0表示区间都没有覆盖。-1表示该区间已经更新 9 bool rev; //异或标记 10 }tree[M << 2]; 11 void build(int root, int l, int r) 12 { 13 tree[root].tag = 0;//初始化 14 tree[root].rev = false; 15 if (l == r) return; 16 int m = (l + r) / 2; 17 build(root*2+1, l, m); 18 build(root*2+2, m + 1, r); 19 } 20 void pushdown(int root,int l,int r) 21 { 22 if (l == r) 23 { 24 if (tree[root].rev) //需要翻转的话就翻转 25 { 26 tree[root].rev = false; 27 tree[root].tag ^= 1; 28 } 29 return; 30 } 31 if (tree[root].tag != -1) //该区间为纯色 32 { 33 if (tree[root].rev) tree[root].tag ^= 1; 34 tree[root*2+1].tag = tree[root*2+2].tag = tree[root].tag; 35 tree[root].tag = -1;//已经下放 36 tree[root*2+1].rev = tree[root*2+2].rev = tree[root].rev = false; 37 } 38 if (tree[root].rev)//需要异或 39 { 40 tree[root].rev = false; 41 if (tree[root*2+1].tag == -1) 42 tree[root*2+1].rev ^= 1;//下放 43 else tree[root*2+1].tag ^= 1;//否则直接标记该区间整个是否覆盖 44 if (tree[root*2+2].tag == -1) 45 tree[root*2+2].rev ^= 1; 46 else tree[root*2+2].tag ^= 1; 47 } 48 } 49 void update(int root,int l,int r,int ll, int rr, int v) 50 { 51 if (l > rr || r < ll)return; 52 if (l >= ll&&r <= rr) 53 { 54 if (v<2) //赋值操作 55 { 56 tree[root].rev = false; 57 tree[root].tag = v; 58 } 59 else //翻转操作 60 { 61 if (tree[root].tag != -1) //区间为纯色,直接翻转即可 62 tree[root].tag ^= 1; 63 else tree[root].rev ^= 1; //异或 64 } 65 return; 66 } 67 int m = (l + r) / 2; 68 pushdown(root,l,r);//向下更新子树 69 if (rr <= m) update(root*2+1, l, m,ll,rr, v); 70 else if (ll>m) update(root*2+2, m+1, r,ll,rr, v); 71 else 72 { 73 update(root*2+1, l, m,ll,rr, v); 74 update(root*2+2, m + 1, r,ll,rr, v); 75 } 76 } 77 bool vis[M]; 78 void Query(int root,int l,int r) 79 { 80 81 if (tree[root].tag != -1) 82 { 83 if (tree[root].tag == 1) 84 for (int i = l; i <= r; i++) 85 vis[i] = true; 86 return; 87 } 88 pushdown(root,l,r); 89 int mid=(l + r)/2; 90 Query(root*2+1,l,mid); 91 Query(root*2+2,mid+1,r); 92 } 93 int main() 94 { 95 //freopen("in.txt", "r", stdin); 96 build(0, 0, M); 97 int l, r; 98 char op, a, b; 99 while (scanf("%c %c%d,%d%c", &op, &a, &l, &r, &b) != -1) 100 { 101 getchar();//读入换行符 102 l <<= 1; if (a == ‘(‘) l++;//开闭区间处理 103 r <<= 1; if (b == ‘)‘) r--; 104 switch (op) 105 { 106 case ‘U‘:update(0,0,M, l, r, 1); break; 107 case ‘I‘:update(0,0,M, 0, l - 1, 0); update(0, 0, M, r + 1, M, 0); break; 108 case ‘D‘:update(0, 0, M, l, r, 0); break; 109 case ‘C‘:update(0, 0, M, 0, l - 1, 0); 110 update(0, 0, M, r + 1, M, 0); 111 update(0, 0, M, l, r, 2); 112 break; 113 case ‘S‘:update(0, 0, M, l, r, 2); 114 } 115 } 116 memset(vis, 0, sizeof(vis)); 117 Query(0, 0, M); 118 bool flag = true; 119 for (int i = 0; i<M; i++) 120 { 121 if (!vis[i]) continue; 122 flag = false; 123 int start = i, end; 124 while (vis[i] && i<M) i++; 125 printf("%c%d,%d%c ", start & 1 ? ‘(‘ : ‘[‘, start / 2, i / 2, (i - 1) & 1 ? ‘)‘ : ‘]‘); 126 } 127 if (flag) puts("empty set"); 128 else printf("\n"); 129 return 0; 130 }
9、ZOJ 1610 Count the Colors
题意:对于一块区域染色,求最后能看到的颜色。用颜色编号+颜色块输出。(输入时涂色的区间为左端点到右端点,即如果分别对[2,3],[4,5]涂相同的颜色,由于3和4之间还有一段空,所以是两个颜色块。这种情况下,区间更新时可以选择把R-1,对答案没有影响)
思路:线段树区间染色问题。
1 #include<iostream> 2 #include<memory.h> 3 #include<algorithm> 4 #include<cstdio> 5 using namespace std; 6 const int maxn = 8010; 7 int tree[maxn << 2]; 8 int n; 9 int tg[maxn]; 10 int cnt[maxn]; 11 void PushDown(int root,int l,int r) 12 { 13 if (tree[root]!= -1) 14 { 15 tree[root * 2 + 1]= tree[root * 2 + 2]= tree[root]; 16 tree[root] = -1; 17 } 18 } 19 void Update(int root, int l, int r, int ll, int rr, int color) 20 { 21 if (l > rr || r < ll) return; 22 if (ll <= l&&r <= rr) 23 { 24 tree[root] = color; 25 return; 26 } 27 PushDown(root,l,r); 28 int mid = (l + r) / 2; 29 Update(root * 2 + 1, l, mid, ll, rr, color); 30 Update(root * 2 + 2, mid + 1, r, ll, rr, color); 31 if (tree[root * 2 + 1]== tree[root * 2 + 2]&& tree[root * 2 + 1]!=-1) tree[root] = tree[root * 2 + 1]; 32 else tree[root] = -1; 33 } 34 void Query(int root, int l, int r) 35 { 36 //PushDown(root,l,r); 37 if (tree[root] != -1) 38 { 39 for (int i = l; i <= r; i++) 40 { 41 tg[i] = tree[root]; 42 } 43 return; 44 } 45 if (l == r)return; 46 int mid = (l + r) / 2; 47 Query(root * 2 + 1, l, mid); 48 Query(root * 2 + 2, mid + 1, r); 49 } 50 int main() 51 { 52 while (~scanf("%d", &n)) 53 { 54 memset(tree, -1, sizeof(tree)); 55 memset(tg, -1, sizeof(tg)); 56 for (int i = 0; i < n; i++) 57 { 58 int l, r, color; 59 scanf("%d%d%d", &l, &r, &color); 60 Update(0, 0, maxn, l, r-1, color);//输入涂色范围为端点,建立时涂色范围改为区域 61 } 62 Query(0, 0, maxn); 63 memset(cnt, 0, sizeof(cnt)); 64 for (int i = 1; i < maxn; i++) 65 { 66 if (tg[i] != tg[i - 1]) 67 { 68 if (tg[i - 1] != -1) 69 { 70 cnt[tg[i - 1]]++; 71 } 72 } 73 } 74 for (int i = 0; i < maxn; i++) 75 { 76 if (cnt[i]) printf("%d %d\n", i, cnt[i]); 77 } 78 printf("\n"); 79 } 80 return 0; 81 }
10、hdu 4027 Can you answer these queries
题意:对于一个数组,每次选择一个区间将其里面的元素都置为原先元素的平方根(向下取整),或询问一段区间的和。
思路:线段树单点更新(由于每个数都不一定相同,需要对区间内的每个数进行独自开方),区间查询。每次更新区间时,先进行一次询问,如果区间内元素都为1,则不必在进行更新。
1 #include<iostream> 2 #include<cmath> 3 using namespace std; 4 const int maxn = 100100; 5 long long tree[maxn*4]; 6 int n,m; 7 void Build(int root, int l, int r) 8 { 9 if (l == r) 10 { 11 scanf("%lld", &tree[root]); 12 return; 13 } 14 int mid = (l + r) / 2; 15 Build(root * 2 + 1, l, mid); 16 Build(root * 2 + 2, mid + 1, r); 17 tree[root] = tree[root * 2 + 1] + tree[root * 2 + 2]; 18 } 19 void Update(int root, int l, int r, int ll, int rr) 20 { 21 if (l > rr || r < ll) return; 22 if (l >= ll&&r <= rr&&l == r) 23 { 24 if (tree[root] > 1) tree[root] = (long long)sqrt(1.0*tree[root]); 25 return; 26 } 27 int mid = (l + r) / 2; 28 Update(root * 2 + 1, l, mid, ll, rr); 29 Update(root * 2 + 2, mid + 1,r, ll, rr); 30 tree[root] = tree[root * 2 + 1] + tree[root * 2 + 2]; 31 } 32 long long Query(int root, int l, int r, int ll, int rr) 33 { 34 if (l > rr || r < ll)return 0; 35 if (l >= ll&&r <= rr)return tree[root]; 36 int mid = (l + r) / 2; 37 return Query(root * 2 + 1, l, mid, ll, rr) + Query(root * 2 + 2, mid + 1, r, ll, rr); 38 } 39 int main() 40 { 41 int Case = 1; 42 while (~scanf("%d",&n)) 43 { 44 printf("Case #%d:\n", Case++); 45 Build(0, 0, n - 1); 46 scanf("%d", &m); 47 for (int i = 0; i < m; i++) 48 { 49 int t, x, y; 50 scanf("%d%d%d", &t, &x, &y); 51 if (x > y) 52 { 53 int t = x; 54 x = y; 55 y = t; 56 } 57 if (t == 0) 58 { 59 if (Query(0, 0, n - 1, x - 1, y - 1) != y - x + 1)//判断一下是否需要继续更新 60 Update(0, 0, n - 1, x - 1, y - 1); 61 } 62 else printf("%lld\n", Query(0, 0, n - 1, x - 1, y - 1)); 63 } 64 printf("\n"); 65 } 66 return 0; 67 }
11、hdu 1540 Tunnel Warfare
题意:每次可以选择破坏一个村庄,或修复最近破坏的一个村庄,或查询某个村庄所在的连续区间(该区间内村庄没有被破坏)的长度。
思路:线段树单点更新,区间查询。在线段树里维护[l,r]之间左边最长连续区间长度和右边最长连续区间长度。
1 #include <stdio.h> 2 #include <string.h> 3 #include <algorithm> 4 #include <math.h> 5 #include <stdlib.h> 6 using namespace std; 7 8 const int maxn = 50000 + 10; 9 10 int n, m; 11 int s[maxn], top;//s为模拟栈 12 //区间合并 13 struct node 14 { 15 int l, r; 16 int ls, rs, ms;//ls,左端最大连续区间,rs右端最大连续区间,ms整个区间内的最大连续长度 17 } a[maxn << 2]; 18 19 void init(int l, int r, int root) 20 { 21 a[root].l = l; 22 a[root].r = r; 23 a[root].ls = a[root].rs = a[root].ms = r - l + 1; 24 if (l != r) 25 { 26 int mid = (l + r) >> 1; 27 init(l, mid, root * 2); 28 init(mid + 1, r, 2 * root + 1); 29 } 30 } 31 32 void insert(int root, int t, int x) 33 { 34 if (a[root].l == a[root].r) 35 { 36 if (x == 1) 37 a[root].ls = a[root].rs = a[root].ms = 1;//修复 38 else 39 a[root].ls = a[root].rs = a[root].ms = 0;//破坏 40 return; 41 } 42 int mid = (a[root].l + a[root].r) >> 1; 43 if (t <= mid) 44 insert(2 * root, t, x); 45 else 46 insert(2 * root + 1, t, x); 47 a[root].ls = a[2 * root].ls;//左区间 48 a[root].rs = a[2 * root + 1].rs;//右区间 49 a[root].ms = max(max(a[2 * root].ms, a[2 * root + 1].ms), a[2 * root].rs + a[2 * root + 1].ls);//父亲区间内的最大区间必定是,左子树最大区间,右子树最大区间,左右子树合并的中间区间,三者中最大的区间值 50 if (a[2 * root].ls == a[2 * root].r - a[2 * root].l + 1)//左子树区间满了的话,父亲左区间要加上右孩子的左区间 51 a[root].ls += a[2 * root + 1].ls; 52 if (a[2 * root + 1].rs == a[2 * root + 1].r - a[2 * root + 1].l + 1)//同理 53 a[root].rs += a[2 * root].rs; 54 } 55 56 int query(int root, int t) 57 { 58 if (a[root].l == a[root].r || a[root].ms == 0 || a[root].ms == a[root].r - a[root].l + 1)//到了叶子节点或者该访问区间为空或者已满都不必要往下走了 59 return a[root].ms; 60 int mid = (a[root].l + a[root].r) >> 1; 61 if (t <= mid) 62 { 63 if (t >= a[2 * root].r - a[2 * root].rs + 1)//判断当前这个数是否在左区间的右连续中,因为t<=mid,看左子树,a[2*i].r-a[2*i].rs+1代表左子树右边连续区间的左边界值,如果t在左子树的右区间内,则要看右子树的左区间有多长并返回 64 return query(2 * root, t) + query(2 * root + 1, mid + 1); 65 else 66 return query(2 * root, t);//如果不在左子树的右边界区间内,则只需要看左子树 67 } 68 else 69 { 70 if (t <= a[2 * root + 1].l + a[2 * root + 1].ls - 1)//同理 71 return query(2 * root + 1, t) + query(2 * root, mid); 72 else 73 return query(2 * root + 1, t); 74 } 75 } 76 77 int main() 78 { 79 int i, j, x; 80 char ch[2]; 81 while (~scanf("%d%d", &n, &m)) 82 { 83 top = 0; 84 init(1, n, 1); 85 while (m--) 86 { 87 scanf("%s", ch); 88 if (ch[0] == ‘D‘) 89 { 90 scanf("%d", &x); 91 s[top++] = x; 92 insert(1, x, 0); 93 } 94 else if (ch[0] == ‘Q‘) 95 { 96 scanf("%d", &x); 97 printf("%d\n", query(1, x)); 98 } 99 else 100 { 101 if (x>0) 102 { 103 x = s[--top]; 104 insert(1, x, 1); 105 } 106 } 107 } 108 } 109 return 0; 110 }
12、hdu 3974 Assign the task
题意:给出一个员工及其直属上司。每次可以把某项任务交给一个主管,他及其下属的工作都会进行该项工作,或询问某个人现在在进行什么任务。
思路:线段树区间更新、单点查询。首先对于员工和上司之间的关系可以建立一棵树,通过从树根DFS把所有员工重新编号,同时记录某个人及其下属的编号区间。
1 ##include<stdio.h> 2 #include<algorithm> 3 #include<vector> 4 using namespace std; 5 6 const int MAXN = 50005; 7 8 int Start[MAXN], End[MAXN];//每个员工所有下属的开始和结束节点,包含本身 9 int index;//DFS用记录节点的编号 10 vector<int> G[MAXN];//保存边 11 12 void DFS(int k) 13 { 14 Start[k] = ++index; 15 for (int i = 0, len = G[k].size(); i<len; i++) 16 DFS(G[k][i]); 17 End[k] = index; 18 } 19 20 struct SegmentTree 21 { 22 int L, R, task; 23 bool isCover; 24 int Mid() 25 { 26 return (L + R) / 2; 27 } 28 }a[MAXN * 4]; 29 30 void BuildTree(int r, int L, int R) 31 { 32 a[r].L = L, a[r].R = R; 33 a[r].task = -1, a[r].isCover = false;//初始化 34 35 if (L == R)return; 36 37 BuildTree(r << 1, L, a[r].Mid()); 38 BuildTree(r << 1 | 1, a[r].Mid() + 1, R); 39 } 40 void Down(int r) 41 {//延迟更新 42 if (a[r].L != a[r].R && a[r].isCover) 43 { 44 a[r << 1].isCover = a[r << 1 | 1].isCover = true; 45 a[r << 1].task = a[r << 1 | 1].task = a[r].task; 46 a[r].isCover = false; 47 } 48 } 49 void Insert(int r, int L, int R, int task) 50 { 51 Down(r); 52 53 if (a[r].L == L && a[r].R == R) 54 { 55 a[r].isCover = true; 56 a[r].task = task; 57 return; 58 } 59 60 if (R <= a[r].Mid()) 61 Insert(r << 1, L, R, task); 62 else if (L > a[r].Mid()) 63 Insert(r << 1 | 1, L, R, task); 64 else 65 { 66 Insert(r << 1, L, a[r].Mid(), task); 67 Insert(r << 1 | 1, a[r].Mid() + 1, R, task); 68 } 69 } 70 int Query(int r, int k) 71 { 72 Down(r); 73 74 if (a[r].L == a[r].R) 75 return a[r].task; 76 77 if (k <= a[r].Mid()) 78 return Query(r << 1, k); 79 else 80 return Query(r << 1 | 1, k); 81 } 82 83 int main() 84 { 85 int T, t = 1; 86 87 scanf("%d", &T); 88 89 while (T--) 90 { 91 int i, N, M, u, v; char s[10]; 92 93 scanf("%d", &N); 94 95 for (i = 1; i <= N; i++) 96 G[i].clear(); 97 bool use[MAXN] = { 0 }; 98 for (i = 1; i<N; i++) 99 { 100 scanf("%d%d", &u, &v);//v是u的直属上司 101 G[v].push_back(u); 102 use[u] = true;//表明下属 103 } 104 105 index = 0; 106 for (i = 1; i <= N; i++)if (!use[i])//总boss 107 { 108 DFS(i); break; 109 } 110 111 BuildTree(1, 1, N); 112 printf("Case #%d:\n", t++); 113 114 scanf("%d", &M); 115 116 while (M--) 117 { 118 scanf("%s", s); 119 120 if (s[0] == ‘C‘) 121 { 122 scanf("%d", &u); 123 printf("%d\n", Query(1, Start[u])); 124 } 125 else 126 { 127 scanf("%d%d", &u, &v); 128 Insert(1, Start[u], End[u], v); 129 } 130 } 131 } 132 return 0; 133 }
13、HDU 1255 覆盖的面积
题意:给出若干个矩形的左上坐标和右下坐标,询问所有矩形覆盖两次及以上的面积和。
思路:对于每个矩形的左侧边和右侧边建立线段,同时记录下所有出现过的y坐标,然后按照y坐标从小到大排序并作为区间建立线段树,每次按照线段的x值从小到大插入线段树。
1 #include<iostream> 2 #include<string> 3 #include<cstdio> 4 #include<cstring> 5 #include<queue> 6 #include<map> 7 #include<cmath> 8 #include<stack> 9 #include<set> 10 #include<vector> 11 #include<algorithm> 12 #define LL long long 13 #define inf 1<<30//极大值 14 using namespace std; 15 const int N = 2005; 16 int n; 17 double y[N]; 18 struct LINE 19 { 20 double x;//竖线的横坐标 21 double y_down, y_up;//竖线的上下端点的y坐标 22 int flag;//矩形左侧还是右侧 23 }line[N]; 24 struct node 25 { 26 double l, r; 27 double x; 28 int cover; 29 bool flag; 30 }node[N << 2]; 31 bool cmp(LINE a, LINE b) 32 { 33 return a.x<b.x; 34 } 35 void build(int rt, int l, int r) 36 { 37 node[rt].l = y[l]; 38 node[rt].r = y[r]; 39 node[rt].x = -1; 40 node[rt].flag = false; 41 node[rt].cover = 0; 42 if (l + 1 == r) 43 { 44 node[rt].flag = true;//叶子结点 45 return; 46 } 47 int mid = (l + r) >> 1; 48 build(rt << 1, l, mid); 49 build(rt << 1 | 1, mid, r); 50 } 51 double Insert_query(int rt, double x, double l, double r, int flag) 52 { 53 if (l >= node[rt].r || r <= node[rt].l) return 0; 54 if (node[rt].flag)//叶子结点,只有到最小区间更新 55 { 56 if (node[rt].cover>1)//超过1次 57 { 58 double pre = node[rt].x; 59 double ans = (x - pre)*(node[rt].r - node[rt].l); 60 node[rt].x = x; 61 node[rt].cover += flag; 62 return ans; 63 } 64 else 65 { 66 node[rt].x = x; 67 node[rt].cover += flag; 68 return 0; 69 } 70 } 71 double ans1, ans2; 72 ans1 = Insert_query(rt << 1, x, l, r, flag); 73 ans2 = Insert_query(rt << 1 | 1, x, l, r, flag); 74 return ans1 + ans2; 75 } 76 int main() 77 { 78 int t; 79 double x1, x2, y1, y2; 80 scanf("%d", &t); 81 while (t--) 82 { 83 scanf("%d", &n); 84 int cnt = -1; 85 for (int i = 0; i<n; i++) 86 { 87 scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2); 88 y[++cnt] = y1; 89 line[cnt].x = x1; 90 line[cnt].y_down = y1; 91 line[cnt].y_up = y2; 92 line[cnt].flag = 1;//矩形左边 93 y[++cnt] = y2; 94 line[cnt].x = x2; 95 line[cnt].y_down = y1; 96 line[cnt].y_up = y2; 97 line[cnt].flag = -1;//矩形右边 98 } 99 sort(y, y + cnt + 1);//把所有y坐标(横线)按y从小到大排列 100 sort(line, line + cnt + 1, cmp);//把所有竖线按x从小到大排列 101 build(1, 0, cnt); 102 double area = 0; 103 for (int i = 0; i <= cnt; i++) 104 { 105 area += Insert_query(1, line[i].x, line[i].y_down, line[i].y_up, line[i].flag); 106 } 107 printf("%.2lf\n", area); 108 } 109 return 0; 110 }
14、uva 11235/poj 3368/hdu 1806 Frequent values
题意:给出一个非递减序列,求[l,r]之间的数的最大出现次数。
思路:将相同的值的数划分为1个块,把每个块编号建立线段树。
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 const int maxn = 100005; 5 const int maxv = 100005; 6 int tree[maxn*4], left[maxn], right[maxn], num[maxn],id[maxv*2]; 7 8 void PushUp(int rt) 9 { 10 tree[rt] = max(tree[rt*2+1], tree[rt*2+2]); 11 } 12 13 void build(int root,int l,int r) 14 { 15 if (l == r) 16 { 17 tree[root] = right[l] - left[l] + 1; 18 return; 19 } 20 int mid = (l + r)/2; 21 build(root*2+1,l,mid); 22 build(root*2+2,mid+1,r); 23 PushUp(root); 24 } 25 26 int Query(int root,int l,int r,int ql,int qr) 27 { 28 if (r<ql || l>qr) return 0; 29 if (ql <= l && r <= qr) 30 { 31 return tree[root]; 32 } 33 int mid = (l + r)/2; 34 return max(Query(root * 2 + 1, l, mid, ql, qr), Query(root * 2 + 2, mid + 1, r, ql, qr)); 35 } 36 37 int main() 38 { 39 int n, q; 40 while (~scanf("%d%d", &n, &q)) 41 { 42 if (n == 0) break; 43 for (int i = 1; i <= n;i++) 44 { 45 scanf("%d", &num[i]); 46 num[i] += 100000; 47 } 48 int cnt = 0; 49 for (int i = 1; i <= n; i++) 50 { 51 int v = num[i],j=i; 52 while (j + 1 <= n&&num[j + 1] == v) j++; 53 left[cnt] = i; 54 right[cnt] = j; 55 id[v] = cnt; 56 i = j; 57 cnt++; 58 } 59 build(0,0,cnt-1); 60 while (q--) 61 { 62 int a, b; 63 scanf("%d%d", &a, &b); 64 int ida = id[num[a]], idb=id[num[b]]; 65 if (idb == ida) printf("%d\n", b - a + 1); 66 else if (idb == ida + 1) printf("%d\n", max(right[ida] - a + 1, b - left[idb] + 1)); 67 else printf("%d\n", max(max(right[ida] - a + 1, b - left[idb] + 1), Query(0,0,cnt-1,ida+1,idb-1))); 68 } 69 } 70 return 0; 71 }
15、LA 3938 Ray, Pass me the dishes!
题意:给出一个数列,求[l,r]区间中最大连续子区间和,并给出最小的子区间范围。
思路:关键在pushup.
1 #include <cstdio> 2 #include <iostream> 3 #include <algorithm> 4 using namespace std; 5 const int maxn = 500100; 6 int lr[maxn << 2], rl[maxn << 2], sr[maxn << 2], sl[maxn << 2]; 7 //lr[]为区间前缀和的末位置,rl[]为区间后缀和的首位置,sr[]、sl[]为区间最大连续和的首尾位置 8 long long maxl[maxn << 2], maxm[maxn << 2], maxr[maxn << 2]; 9 //maxl[]为最大前缀和,maxm为最大连续子区间和,maxr为最大后缀和 10 long long c[maxn]; 11 //c[i]为输入数组的前缀和 12 struct node 13 { 14 int x, y, lr, rl; 15 long long sum, maxl, maxr; 16 }; 17 18 long long Max(long long a, long long b, long long c) 19 { 20 return max(a, max(b, c)); 21 } 22 23 void PushUp(int root,int l, int r) 24 { 25 int mid = (l + r)/2; 26 //处理根区间最大前缀和 27 if (maxl[root*2+1] + c[mid] - c[l - 1] <= maxl[root*2]) 28 {//如果【左区间最大前缀和】大于等于【左区间和+右区间最大前缀和】 29 maxl[root] = maxl[root*2]; 30 lr[root] = lr[root*2]; 31 } 32 else 33 { 34 maxl[root] = maxl[root*2+1] + c[mid] - c[l - 1]; 35 lr[root] = lr[root*2+1]; 36 } 37 //处理根区间最大后缀和 38 if (maxr[root << 1] + c[r] - c[mid] < maxr[root << 1 | 1]) 39 {//如果【右区间最大后缀和】大于【左区间最大后缀和+右区间和】 40 maxr[root] = maxr[root*2+1]; 41 rl[root] = rl[root*2+1]; 42 } 43 else 44 { 45 maxr[root] = maxr[root*2] + c[r] - c[mid]; 46 rl[root] = rl[root*2]; 47 } 48 //处理根区间最大子区间和 49 if (maxm[root*2] >= maxm[root*2+1]) 50 {//如果【左区间最大子区间连续和】大于等于【右区间最大子区间连续和】 51 maxm[root] = maxm[root*2], sl[root] = sl[root*2], sr[root] = sr[root*2]; 52 } 53 else 54 { 55 maxm[root] = maxm[root*2+1], sl[root] = sl[root*2+1], sr[root] = sr[root*2+1]; 56 } 57 if (maxm[root] < maxr[root*2] + maxl[root*2+1] || (maxm[root] == maxr[root*2] + maxl[root*2+1] && (rl[root *2] < sl[root] || (rl[root*2] == sl[root] && lr[root*2+1] <= sr[root])))) 58 {//如果(【左区间最大后缀和】+【右区间最大前缀和】大于【根最大子区间连续和】)或者两者相等但是区间范围前者小 59 maxm[root] = maxr[root*2] + maxl[root*2+1]; 60 sl[root] = rl[root*2]; 61 sr[root] = lr[root*2+1]; 62 } 63 } 64 65 node query(int root, int l, int r,int ql, int qr) 66 { 67 node res; 68 if (ql <= l && qr >= r) 69 { 70 res.x = sl[root]; 71 res.y = sr[root]; 72 res.lr = lr[root]; 73 res.rl = rl[root]; 74 res.maxl = maxl[root]; 75 res.maxr = maxr[root]; 76 res.sum = maxm[root]; 77 return res; 78 } 79 int mid= (l + r)/2; 80 if (qr <= mid) return query(root*2,l,mid,ql,qr); 81 else if (ql > mid) return query(root*2+1,mid+1,r,ql,qr); 82 else 83 { 84 node a, b; 85 a = query(root * 2, l, mid, ql, qr); 86 b = query(root * 2 + 1, mid + 1, r, ql, qr); 87 //相当于node类型的pushup 88 if (b.maxl + c[mid] - c[l - 1] <= a.maxl) 89 { 90 res.maxl = a.maxl; 91 res.lr = a.lr; 92 } 93 else 94 { 95 res.maxl = b.maxl + c[mid] - c[l - 1]; 96 res.lr = b.lr; 97 } 98 if (a.maxr + c[r] - c[mid] < b.maxr) 99 { 100 res.maxr = b.maxr; 101 res.rl = b.rl; 102 } 103 else 104 { 105 res.maxr = a.maxr + c[r] - c[mid]; 106 res.rl = a.rl; 107 } 108 if (a.sum >= b.sum) 109 { 110 res.sum = a.sum, res.x = a.x, res.y = a.y; 111 } 112 else 113 { 114 res.sum = b.sum, res.x = b.x, res.y = b.y; 115 } 116 if (res.sum < a.maxr + b.maxl || (res.sum == a.maxr + b.maxl && (res.x > a.rl || (res.x == a.rl && res.y > b.lr)))) 117 { 118 res.sum = a.maxr + b.maxl; 119 res.x = a.rl; 120 res.y = b.lr; 121 } 122 return res; 123 } 124 } 125 126 void Build(int root,int l,int r) 127 { 128 if (l == r) 129 { 130 sl[root] = lr[root] = rl[root] = sr[root] = l; 131 maxl[root] = maxr[root] = maxm[root] = c[l] - c[l - 1]; 132 return; 133 } 134 int mid = (l + r) >> 1; 135 Build(root*2,l,mid); 136 Build(root*2+1,mid+1,r); 137 PushUp(root,l, r); 138 } 139 140 int main() 141 { 142 int Case = 1, n, m; 143 while (~scanf("%d%d", &n, &m)) 144 { 145 c[0] = 0; 146 for (int i = 1; i <= n; i++) 147 { 148 scanf("%lld", &c[i]); 149 c[i] += c[i - 1]; 150 } 151 Build(1,1,n); 152 printf("Case %d:\n", Case++); 153 while (m--) 154 { 155 int a, b; 156 scanf("%d%d", &a, &b); 157 node ans = query(1,1,n,a, b); 158 printf("%d %d\n", ans.x, ans.y); 159 } 160 } 161 return 0; 162 }
16、UVA 11992 Fast Matrix Operations
题意:给出一个矩阵,每次可以add或set指定的子矩阵,或者询问一个子矩阵的和、最大值、最小值。
思路:由于行数不超过20,直接建r*c*4大小的线段树,每次对矩阵操作时分别对每一行操作。更新时注意先set后add.
1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 const int maxn = 1000100; 5 const int INF = 0x7fffffff; 6 struct node 7 { 8 int maxv, minv, sumv; 9 int setv, addv; 10 node(int mx=0,int mn=0,int sm=0,int st=-1,int ad=0):maxv(mx),minv(mn),sumv(sm),setv(st),addv(ad){ } 11 }tree[maxn<<2]; 12 void Setv(int root,int l,int r, int v) 13 { 14 tree[root].addv = 0; 15 tree[root].setv = v; 16 tree[root].sumv = (r - l + 1)*v; 17 tree[root].maxv = v; 18 tree[root].minv = v; 19 } 20 void Addv(int root, int l, int r, int v) 21 { 22 tree[root].addv += v; 23 tree[root].sumv += (r - l + 1)*v; 24 tree[root].maxv += v; 25 tree[root].minv += v; 26 } 27 void PushDown(int root, int l, int r) 28 { 29 int mid = (l + r) / 2; 30 if (tree[root].setv >= 0) 31 { 32 Setv(root * 2, l, mid, tree[root].setv); 33 Setv(root * 2+1, mid+1,r, tree[root].setv); 34 tree[root].setv = -1; 35 } 36 if (tree[root].addv > 0) 37 { 38 Addv(root * 2, l, mid, tree[root].addv); 39 Addv(root * 2 + 1, mid + 1, r, tree[root].addv); 40 tree[root].addv = 0; 41 } 42 } 43 void PushUp(int root, int l, int r) 44 { 45 tree[root].sumv = tree[root * 2].sumv + tree[root * 2 + 1].sumv; 46 tree[root].maxv = max(tree[root * 2].maxv, tree[root * 2 + 1].maxv); 47 tree[root].minv = min(tree[root * 2].minv, tree[root * 2 + 1].minv); 48 tree[root].setv = -1, tree[root].addv = 0; 49 } 50 void Build(int root, int l, int r) 51 { 52 if (l == r) 53 { 54 tree[root] = node(0, 0, 0, -1, 0); 55 return; 56 } 57 int mid = (l + r) / 2; 58 Build(root * 2, l, mid); 59 Build(root * 2 + 1, mid + 1, r); 60 PushUp(root, l, r); 61 } 62 void Update(int root, int l, int r, int ul, int ur,int v, int flag) 63 {//flag=1,add;flag=2,set 64 if (l > ur || r < ul) return; 65 if (ul <= l&&r <= ur) 66 { 67 if (flag == 1) Addv(root, l, r, v); 68 else Setv(root, l, r, v); 69 return; 70 } 71 PushDown(root, l, r); 72 int mid = (l + r) / 2; 73 Update(root * 2, l, mid, ul, ur, v, flag); 74 Update(root * 2 + 1, mid + 1, r, ul, ur, v, flag); 75 PushUp(root, l, r); 76 } 77 node Query(int root, int l, int r, int ql, int qr) 78 { 79 if (l > qr || r < ql) return node(-INF,INF,0,-1,0); 80 if (ql <= l&&r <= qr) return tree[root]; 81 PushDown(root, l, r); 82 int mid = (l + r) / 2; 83 node n1 = Query(root * 2, l, mid, ql, qr); 84 node n2 = Query(root * 2 + 1, mid + 1, r, ql, qr); 85 return node(max(n1.maxv, n2.maxv), min(n1.minv, n2.minv), n1.sumv + n2.sumv); 86 } 87 int main() 88 { 89 int r, c, q; 90 while (~scanf("%d%d%d", &r, &c, &q)) 91 { 92 Build(1, 1, r*c); 93 while (q--) 94 { 95 int sign; 96 scanf("%d", &sign); 97 if (sign != 3) 98 { 99 int x1, x2, y1, y2, v; 100 scanf("%d%d%d%d%d", &x1, &y1, &x2, &y2, &v); 101 for (int i = x1 - 1; i < x2; i++) 102 { 103 Update(1, 1, r*c, i*c + y1, i*c + y2, v, sign); 104 } 105 } 106 else 107 { 108 node ans(-INF,INF,0); 109 int x1, x2, y1, y2; 110 scanf("%d%d%d%d", &x1, &y1, &x2, &y2); 111 for (int i = x1 - 1; i < x2; i++) 112 { 113 node tmp = Query(1, 1, r*c, i*c + y1, i*c + y2); 114 ans.maxv = max(ans.maxv, tmp.maxv); 115 ans.minv = min(ans.minv, tmp.minv); 116 ans.sumv += tmp.sumv; 117 } 118 printf("%d %d %d\n", ans.sumv, ans.minv, ans.maxv); 119 } 120 } 121 } 122 return 0; 123 }
以上是关于线段树专题(不定期更新)的主要内容,如果未能解决你的问题,请参考以下文章
线段树专题—ZOJ1610 Count the Colors