HDU 4819:Mosaic(线段树套线段树)
http://acm.hdu.edu.cn/showproblem.php?pid=4819
题意:给出一个矩阵,然后q个询问,每个询问有a,b,c,代表(a,b)这个点上下左右c/2的矩形区域内的(最大值+最小值)/2是多少,并且将(a,b)的值替换成这个答案。
思路:很久以前被暴力跑过去的一道题,今天怎么交也过不去。。。果然是人品爆发了。
学了一下树套树,一开始觉得挺容易理解,但是后面PushUp那里挺难懂的(对我来说)。
我的理解:
对于每个线段树的结点开一棵线段树,即tree[x][y],x代表的是行的信息,y代表的是列的信息。
觉得PushUp难懂的原因是不知道行上的非叶子结点和列上的非叶子结点是怎么更新的。
1 void PushUp1(int x, int y) { 2 tree[x][y].small = min(tree[x<<1][y].small, tree[x<<1|1][y].small); 3 tree[x][y].big = max(tree[x<<1][y].big, tree[x<<1|1][y].big); 4 } 5 6 void PushUp2(int x, int y) { 7 tree[x][y].small = min(tree[x][y<<1].small, tree[x][y<<1|1].small); 8 tree[x][y].big = max(tree[x][y<<1].big, tree[x][y<<1|1].big); 9 } 10 11 void Update1(int x, int leaf, int rt, int l, int r, int id, int val) { 12 if(l == r) { 13 if(leaf) { tree[x][rt].small = tree[x][rt].big = val; return ; } 14 PushUp1(x, rt); // 列相同的时候并且行不是叶子结点的时候去更新行的线段树的状态 15 return ; 16 } 17 int m = (l + r) >> 1; 18 if(id <= m) Update1(x, leaf, lson, id, val); 19 else Update1(x, leaf, rson, id, val); 20 PushUp2(x, rt); 21 } 22 23 void Update2(int rt, int l, int r, int xx, int yy, int val) { 24 if(l == r) { 25 Update1(rt, 1, 1, 1, n, yy, val); 26 return ; 27 } 28 int m = (l + r) >> 1; 29 if(xx <= m) Update2(lson, xx, yy, val); 30 else Update2(rson, xx, yy, val); 31 Update1(rt, 0, 1, 1, n, yy, val); 32 }
首先看Update2,这是更新行的线段树信息,和普通线段树一样,只不过是普通的PushUp改成了Update1,插入操作改成了Update1,因此理解Update1就好了。
对于既是列的叶子结点又是行的叶子结点的结点,是对应于矩阵一个点的结点,因此对其赋值修改。
对于是列的叶子结点但是不是行的叶子结点的结点,我们将其行的信息像平时维护一维线段树一样,将行的信息PushUp。
对于不是列的叶子结点的结点,它可以储存列的区间信息,因此将列的信息PushUp。
那么像我之前的疑问,即不是列的叶子结点又不是行的叶子结点的信息在哪里维护。。。
注意原本的PushUp操作变成了Update1,即对于每个行结点,都会去更新对应的那棵线段树,而且是从底向上,因此信息都会被更新。
还优化了一下一开始的读入插入,用类似于Update的Build函数,可以在Build的时候行为叶子列为叶子的时候读入,这样操作为O(n^2)的复杂度,普通的Update插入需要O(n^2 logn^2),跑之后快了一倍的时间。
下面是所有代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define N 800 4 #define INF 1000000007 5 #define lson rt<<1, l, m 6 #define rson rt<<1|1, m + 1, r 7 struct node { 8 int small, big; 9 } tree[N<<4][N<<4]; 10 int big, small, n; 11 12 void PushUp1(int x, int y) { 13 tree[x][y].small = min(tree[x<<1][y].small, tree[x<<1|1][y].small); 14 tree[x][y].big = max(tree[x<<1][y].big, tree[x<<1|1][y].big); 15 } 16 17 void PushUp2(int x, int y) { 18 tree[x][y].small = min(tree[x][y<<1].small, tree[x][y<<1|1].small); 19 tree[x][y].big = max(tree[x][y<<1].big, tree[x][y<<1|1].big); 20 } 21 22 void Build1(int x, int leaf, int rt, int l, int r) { 23 if(l == r) { 24 if(leaf) { scanf("%d", &tree[x][rt].big), tree[x][rt].small = tree[x][rt].big; return ; } 25 PushUp1(x, rt); return ; 26 } 27 int m = (l + r) >> 1; 28 Build1(x, leaf, lson); Build1(x, leaf, rson); 29 PushUp2(x, rt); 30 } 31 32 void Build2(int rt, int l, int r) { 33 if(l == r) { Build1(rt, 1, 1, 1, n); return ; } 34 int m = (l + r) >> 1; 35 Build2(lson); Build2(rson); 36 Build1(rt, 0, 1, 1, n); 37 } 38 39 void Query1(int x, int rt, int l, int r, int y1, int y2) { 40 if(y1 <= l && r <= y2) { 41 big = max(big, tree[x][rt].big); 42 small = min(small, tree[x][rt].small); 43 return ; 44 } 45 int m = (l + r) >> 1; 46 if(y1 <= m) Query1(x, lson, y1, y2); 47 if(m < y2) Query1(x, rson, y1, y2); 48 } 49 50 void Query2(int rt, int l, int r, int x1, int x2, int y1, int y2) { 51 if(x1 <= l && r <= x2) { 52 Query1(rt, 1, 1, n, y1, y2); 53 return ; 54 } 55 int m = (l + r) >> 1; 56 if(x1 <= m) Query2(lson, x1, x2, y1, y2); 57 if(m < x2) Query2(rson, x1, x2, y1, y2); 58 } 59 60 void Update1(int x, int leaf, int rt, int l, int r, int id, int val) { 61 if(l == r) { 62 if(leaf) { tree[x][rt].small = tree[x][rt].big = val; return ; } 63 PushUp1(x, rt); // 列相同的时候并且行不是叶子结点的时候去更新行的线段树的状态 64 return ; 65 } 66 int m = (l + r) >> 1; 67 if(id <= m) Update1(x, leaf, lson, id, val); 68 else Update1(x, leaf, rson, id, val); 69 PushUp2(x, rt); 70 } 71 72 void Update2(int rt, int l, int r, int xx, int yy, int val) { 73 if(l == r) { 74 Update1(rt, 1, 1, 1, n, yy, val); 75 return ; 76 } 77 int m = (l + r) >> 1; 78 if(xx <= m) Update2(lson, xx, yy, val); 79 else Update2(rson, xx, yy, val); 80 Update1(rt, 0, 1, 1, n, yy, val); 81 } 82 83 int main() { 84 int t; scanf("%d", &t); 85 for(int cas = 1; cas <= t; cas++) { 86 scanf("%d", &n); 87 Build2(1, 1, n); 88 int q; scanf("%d", &q); 89 printf("Case #%d:\n", cas); 90 while(q--) { 91 int a, b, c; scanf("%d%d%d", &a, &b, &c); 92 int x1 = max(1, a - c / 2), x2 = min(n, a + c / 2); 93 int y1 = max(1, b - c / 2), y2 = min(n, b + c / 2); 94 big = -INF, small = INF; 95 Query2(1, 1, n, x1, x2, y1, y2); 96 int now = (big + small) / 2; 97 printf("%d\n", now); 98 Update2(1, 1, n, a, b, now); 99 } 100 } 101 return 0; 102 }