NOI 2012 魔幻棋盤 | 二維差分 + 二維線段樹


題目:luogu 2086

二維線段樹,按套路差分原矩陣,gcd( x1, x2, ……, xn ) = gcd( x, x2 - x, ……, xn - xn-1 ),必須要有一個原數 xi,恰好每次詢問都包含一個固定點 ( X , Y ),差分以它為中心就可以保證它是原值。以 e 為中心的二維差分如圖。

對於一維序列,修改區間 [ l , r ] 只需修改差分后的 l 和 r + 1 兩點。那么對於二維,差分后的修改如下所示:

 

中間的灰色格子為守衛者所在地(為方便表示多個區域把它拆成了 9 格),以它為差分中心,若修改矩陣在它的右下方,即黃色區域,設它的左上角坐標為 ( x1, y1),右下角坐標 ( x2, y2 ),則需在 ( x1, y1 ) 處 + c,( x1, y2+1 ) 處 - c,( x2+1, y1 ) 處 - c,( x2 + 1, y2 + 1) 處 +c。

其他區域同理。再加上守衛者正上方、正下方、正左方、正右方的區域,一共 8 種情況。如果一個修改矩陣包含多個區域,就將其划分為若干個子矩陣處理。

這樣就將巨慢的區間修改轉換成了單點修改,每修改完一個點,pushup( ) 維護 gcd 即可。

另外讀入的時候,500000 * 500000 的二維數組是開不了的,需要動態分配內存;或者開一個 500000 的一維數組讀入,判斷每一行的末端在哪里。

 

討論的時候有些麻煩,下面列一下大致的情況:(黃色表示修改的區域,綠色加, 紅色減)

仔細觀察后,發現可分為三種情況:① 包含守衛者 ( X , Y );② 包含 X 行或 Y 列;③ 只包含左上或左下、右上或右下區域。

細節諸多:

差分的時候不能用原數組減,因為前面的值會變,需要另開一個數組保存;

返回 gcd 的時候要取絕對值;二維線段樹內層空間要開大。

 

  1 #include <cstdio>
  2 #include <string>
  3 
  4 const int N = 500005;
  5 typedef long long ll;
  6 
  7 int n, m; ll a[N], b[N];
  8 
  9 ll read() {
 10     ll x = 0, f = 1;
 11     char c = getchar();
 12     while (!isdigit(c)) {
 13         if (c == '-') f = -1;
 14         c = getchar();
 15     }
 16     while (isdigit(c)) {
 17         x = (x << 3) + (x << 1) + (c ^ 48);
 18         c = getchar();
 19     }
 20     return x * f;
 21 }
 22 
 23 ll abs(ll x) {
 24     if (x >= 0) return x; return -x;
 25 }
 26 
 27 ll gcd(ll x, ll y) {
 28     if (y) return gcd(y, x % y); return abs(x);
 29 }
 30 
 31 struct inNode {
 32     ll val;
 33     inNode *ls, *rs;
 34     inNode() : val(0), ls(NULL), rs(NULL) {}
 35 } inPool[N << 2];
 36 
 37 inNode *newInNode () {
 38     static int cnt = 0;
 39     return &inPool[++cnt];
 40 }
 41 
 42 void buildY(inNode *&cur, int l, int r, int x) {
 43     if (!cur) cur = newInNode();
 44     if (l == r) cur->val = a[(x - 1) * m + l];
 45     else {
 46         int mid = l + ((r - l) >> 1);
 47         buildY(cur->ls, l, mid, x);
 48         buildY(cur->rs, mid + 1, r, x);
 49         cur->val = gcd(cur->ls->val, cur->rs->val);
 50     }
 51 }
 52 
 53 void updateY(inNode *&cur, int l, int r, int y, ll val) {
 54     if (l == r) cur->val += val;
 55     else {
 56         int mid = l + ((r - l) >> 1);
 57         if (y <= mid) updateY(cur->ls, l, mid, y, val);
 58         else updateY(cur->rs, mid + 1, r, y, val);
 59         cur->val = gcd(cur->ls ? cur->ls->val : 0, cur->rs ? cur->rs->val : 0);
 60     }
 61 }
 62 
 63 ll queryY(inNode *&cur, int l, int r, int y1, int y2) {
 64     if (y1 <= l && r <= y2) return cur->val;
 65     int mid = l + ((r - l) >> 1); ll res = 0;
 66     if (y1 <= mid) res = gcd(res, queryY(cur->ls, l, mid, y1, y2));
 67     if (mid < y2) res = gcd(res, queryY(cur->rs, mid + 1, r, y1, y2));
 68     return res;
 69 }
 70 
 71 struct outNode {
 72     inNode *root;
 73     outNode *ls, *rs;
 74     outNode() : root(NULL), ls(NULL), rs(NULL) {}
 75 } outPool[N << 2], *root;
 76 
 77 outNode *newOutNode() {
 78     static int cnt = 0;
 79     return &outPool[++cnt];
 80 }
 81 
 82 void maintain(inNode *&cur, inNode *&lc, inNode *&rc, int l, int r) {
 83     if (!cur) cur = newInNode();
 84     if (l == r) cur->val = gcd(lc->val, rc->val);
 85     else {
 86         int mid = l + ((r - l) >> 1);
 87         maintain(cur->ls, lc->ls, rc->ls, l, mid);
 88         maintain(cur->rs, lc->rs, rc->rs, mid + 1, r);
 89         cur->val = gcd(cur->ls->val ,cur->rs->val);
 90     }
 91 }
 92 
 93 void buildX(outNode *&cur, int l, int r) {
 94     if (!cur) cur = newOutNode();
 95     if (l == r) buildY(cur->root, 1, m, l);
 96     else {
 97         int mid = l + ((r - l) >> 1);
 98         buildX(cur->ls, l, mid);
 99         buildX(cur->rs, mid + 1, r);
100         maintain(cur->root, cur->ls->root, cur->rs->root, 1, m);
101     }
102 }
103 
104 void change(inNode *&cur, int l, int r, int y, ll val) {
105     if (l == r) cur->val = val;
106     else {
107         int mid = l + ((r - l) >> 1);
108         if (y <= mid) change(cur->ls, l, mid, y, val);
109         else change(cur->rs, mid + 1, r, y, val);
110         cur->val = gcd(cur->ls ? cur->ls->val : 0, cur->rs ? cur->rs->val : 0);
111     } 
112 }
113 
114 void updateX(outNode *&cur, int l, int r, int x, int y, ll val) {
115     if (x <= 0 || y <= 0 || x > n || y > m) return;
116     if (l == r) updateY(cur->root, 1, m, y, val);
117     else {
118         int mid = l + ((r - l) >> 1); ll lv = 0, rv = 0;
119         if (x <= mid) updateX(cur->ls, l, mid, x, y, val);
120         else updateX(cur->rs, mid + 1, r, x, y, val);
121         if (cur->ls) lv = queryY(cur->ls->root, 1, m, y, y);
122         if (cur->rs) rv = queryY(cur->rs->root, 1, m, y, y);
123         change(cur->root, 1, m, y, gcd(lv, rv));
124     }
125 }
126 
127 ll queryX(outNode *&cur, int l, int r, int x1, int x2, int y1, int y2) {
128     if (x1 <= l && r <= x2) return queryY(cur->root, 1, m, y1, y2);
129     int mid = l + ((r - l) >> 1); ll res = 0;
130     if (x1 <= mid) res = gcd(res, queryX(cur->ls, l, mid, x1, x2, y1, y2));
131     if (mid < x2) res = gcd(res, queryX(cur->rs, mid + 1, r, x1, x2, y1, y2));
132     return res;
133 }
134 
135 int main() {
136     n = read(), m = read();    int X = read(), Y = read(), T = read();
137     for (int i = 1; i <= n * m; ++ i) a[i] = read();
138     for (int i = 1; i <= n * m; ++ i) {
139         if ((i - 1) % m + 1 < Y) b[i] = a[i] - a[i + 1];
140         else if ((i - 1) % m + 1 > Y) b[i] = a[i] - a[i - 1];
141         else b[i] = a[i];
142     }
143     for (int i = 1; i <= n * m; ++ i) {
144         if ((i - 1) / m + 1 < X) a[i] = b[i] - b[i + m];
145         else if ((i - 1) / m + 1 > X) a[i] = b[i] - b[i - m];
146         else a[i] = b[i];
147     }
148     buildX(root, 1, n);
149     while (T--) {
150         int opt = read(), x1 = read(), y1 = read(), x2 = read(), y2 = read();
151         if (opt == 0) {
152             x1 = X - x1, x2 = X + x2, y1 = Y - y1, y2 = Y + y2;
153             printf("%lld\n", queryX(root, 1, n, x1, x2, y1, y2));
154         } else {
155             ll val = read();
156             if (x1 <= X && x2 >= X && y1 <= Y && y2 >= Y) {    //包含(X,Y) 
157                 updateX(root, 1, n, X, Y, val);
158                 updateX(root, 1, n, x1 - 1, y1 - 1, val);
159                 updateX(root, 1, n, x1 - 1, y2 + 1, val);
160                 updateX(root, 1, n, x2 + 1, y1 - 1, val);
161                 updateX(root, 1, n, x2 + 1, y2 + 1, val);
162                 updateX(root, 1, n, x1 - 1, Y, -val);
163                 updateX(root, 1, n, x2 + 1, Y, -val);
164                 updateX(root, 1, n, X, y1 - 1, -val);
165                 updateX(root, 1, n, X, y2 + 1, -val);
166             } else if (y1 <= Y && y2 >= Y) {                //包含Y列 
167                 if (x1 < X) {                                //在(X,Y)上方 
168                     updateX(root, 1, n, x2, Y, val);
169                     updateX(root, 1, n, x1 - 1, y1 - 1, val);
170                     updateX(root, 1, n, x1 - 1, y2 + 1, val);
171                     updateX(root, 1, n, x1 - 1, Y, -val);
172                     updateX(root, 1, n, x2, y1 - 1, -val);
173                     updateX(root, 1, n, x2, y2 + 1, -val);
174                 } else {                                    //在(X,Y)下方 
175                     updateX(root, 1, n, x1, Y, val);
176                     updateX(root, 1, n, x2 + 1, y1 - 1, val);
177                     updateX(root, 1, n, x2 + 1, y2 + 1, val);
178                     updateX(root, 1, n, x2 + 1, Y, -val);
179                     updateX(root, 1, n, x1, y1 - 1, -val);
180                     updateX(root, 1, n, x1, y2 + 1, -val);
181                 }
182             } else if (x1 <= X && x2 >= X) {                //包含X行 
183                 if (y1 < Y) {                                //在(X,Y)左邊 
184                     updateX(root, 1, n, X, y2, val);
185                     updateX(root, 1, n, x1 - 1, y1 - 1, val);
186                     updateX(root, 1, n, x2 + 1, y1 - 1, val);
187                     updateX(root, 1, n, X, y1 - 1, -val);
188                     updateX(root, 1, n, x1 - 1, y2, -val);
189                     updateX(root, 1, n, x2 + 1, y2, -val);
190                 } else {                                    //在(X,Y)右邊 
191                     updateX(root, 1, n, X, y1, val);
192                     updateX(root, 1, n, x1 - 1, y2 + 1, val);
193                     updateX(root, 1, n, x2 + 1, y2 + 1, val);
194                     updateX(root, 1, n, X, y2 + 1, -val);
195                     updateX(root, 1, n, x1 - 1, y1, -val);
196                     updateX(root, 1, n, x2 + 1, y1, -val);
197                 }
198             } else if (x1 < X && y1 < Y) {                    //左上區域 
199                 updateX(root, 1, n, x2, y2, val);
200                 updateX(root, 1, n, x1 - 1, y1 - 1, val);
201                 updateX(root, 1, n, x1 - 1, y2, -val);
202                 updateX(root, 1, n, x2, y1 - 1, -val);
203             } else if (x1 < X && y1 > Y) {                    //右上區域 
204                 updateX(root, 1, n, x2, y1, val);
205                 updateX(root, 1, n, x1 - 1, y2 + 1, val);
206                 updateX(root, 1, n, x1 - 1, y1, -val);
207                 updateX(root, 1, n, x2, y2 + 1, -val);
208             } else if (x1 > X && y1 < Y) {                    //左下區域 
209                 updateX(root, 1, n, x1, y2, val);
210                 updateX(root, 1, n, x2 + 1, y1 - 1, val);
211                 updateX(root, 1, n, x1, y1 - 1, -val);
212                 updateX(root, 1, n, x2 + 1, y2, -val);
213             } else {                                        //右下區域 
214                 updateX(root, 1, n, x1, y1, val);
215                 updateX(root, 1, n, x2 + 1, y2 + 1, val);
216                 updateX(root, 1, n, x1, y2 + 1, -val);
217                 updateX(root, 1, n, x2 + 1, y1, -val);
218             }
219         }
220     }
221     return 0;
222 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM