二維樹狀陣列總結
概念
維護原二維數列的差分數列,從而用二維樹狀陣列進行單點修改,求二維字首和等操作。
例題
[Luogu] P4514 上帝造題的七分鐘
Description
對\(n\times{m}\)矩陣進行區域加,區域求和。\((1≤n≤2048,1≤m≤2048,−500≤d≤500)\)
Solution
\(\begin{aligned}&\text {設} a_{x, y}=\sum_{i=1}^{x} \sum_{j=1}^{y} \Delta a_{x, y}, \text { 則 }\\&\Delta a_{x, y}=a_{x, y}+a_{x-1, y-1}-a_{x, y-1}-a_{x-1, y} \quad \text { (最終求區間和時同理})\\&\sum_{x=1}^{n} \sum_{y=1}^{m} a_{x, y}=\sum_{x=1}^{n} \sum_{y=1}^{m} \sum_{i=1}^{x} \sum_{j=1}^{y} \Delta a_{x, y}=\sum_{x=1}^{n}(n-x+1) \sum_{y=1}^{m}(m-y+1) \Delta a_{x, y}\\&=(n+1)(m+1) \sum_{x=1}^{n} \sum_{y=1}^{m} \Delta a_{x, y}-(n+1) \sum_{x=1}^{n} \sum_{y=1}^{m} y \Delta a_{x, y}-(m+1) \sum_{x=1}^{n} \sum_{y=1}^{m} x \Delta a_{x, y}+\sum_{x=1}^{n} \sum_{y=1}^{m} x y \Delta a_{x, y}\\&\text { 二維樹狀陣列維護 } \sum_{x=1}^{n} \sum_{y=1}^{m} \Delta a_{x, y}, \sum_{x=1}^{n} \sum_{y=1}^{m} y \Delta a_{x, y}, \sum_{x=1}^{n} \sum_{y=1}^{m} x \Delta a_{x, y},\sum_{x=1}^{n} \sum_{y=1}^{m} x y \Delta a_{x, y} \text { 即可 }\end{aligned}\)
Code
#include <bits/stdc++.h> using namespace std; #define lowbit(x) ((x) & (-x)) int n, m, t[2050][2050][5]; char ch[2]; int read() { int x = 0, fl = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') fl = -1; ch = getchar();} while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + ch - '0'; ch = getchar();} return x * fl; } void add(int x, int y, int d, int tp) { for (int i = x; i <= n; i += lowbit(i)) for (int j = y; j <= m; j += lowbit(j)) t[i][j][tp] += d; return; } int ask(int x, int y, int tp) { int sum = 0; for (int i = x; i; i -= lowbit(i)) for (int j = y; j; j -= lowbit(j)) sum += t[i][j][tp]; return sum; } void modify(int x, int y, int d) { add(x, y, d, 1); add(x, y, d * x, 2); add(x, y, d * y, 3); add(x, y, d * x * y, 4); return; } int query(int x, int y) { return (x + 1) * (y + 1) * ask(x, y, 1) - (y + 1) * ask(x, y, 2) - (x + 1) * ask(x, y, 3) + ask(x, y, 4); } int main() { cin >> ch >> n >> m; while (~scanf("%s", ch)) { int a, b, c, d, x; if (ch[0] == 'L') { cin >> a >> b >> c >> d >> x; modify(a, b, x); modify(a, d + 1, -x); modify(c + 1, b, -x); modify(c + 1, d + 1, x); } else { cin >> a >> b >> c >> d; int res = query(c, d) - query(c, b - 1) - query(a - 1, d) + query(a - 1, b - 1); printf("%d\n", res); } } return 0; }
[Luogu] CF341D Iahub and Xors
Description
對\(n\times{n}\)矩陣,\(m\)次進行區域異或\(d\),區域求異或和。\((1≤n≤1000,1≤m≤10^5,0≤d≤2^{62})\)
Solution
和上一題挺像的。注意到同一個數異或偶數次是\(0\),異或奇數次是它本身。
Code
#include <bits/stdc++.h> using namespace std; #define lowbit(x) ((x) & (-x)) int n, m, t[2050][2050][5]; char ch[2]; int read() { int x = 0, fl = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') fl = -1; ch = getchar();} while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + ch - '0'; ch = getchar();} return x * fl; } void add(int x, int y, int d, int tp) { for (int i = x; i <= n; i += lowbit(i)) for (int j = y; j <= m; j += lowbit(j)) t[i][j][tp] += d; return; } int ask(int x, int y, int tp) { int sum = 0; for (int i = x; i; i -= lowbit(i)) for (int j = y; j; j -= lowbit(j)) sum += t[i][j][tp]; return sum; } void modify(int x, int y, int d) { add(x, y, d, 1); add(x, y, d * x, 2); add(x, y, d * y, 3); add(x, y, d * x * y, 4); return; } int query(int x, int y) { return (x + 1) * (y + 1) * ask(x, y, 1) - (y + 1) * ask(x, y, 2) - (x + 1) * ask(x, y, 3) + ask(x, y, 4); } int main() { cin >> ch >> n >> m; while (~scanf("%s", ch)) { int a, b, c, d, x; if (ch[0] == 'L') { cin >> a >> b >> c >> d >> x; modify(a, b, x); modify(a, d + 1, -x); modify(c + 1, b, -x); modify(c + 1, d + 1, x); } else { cin >> a >> b >> c >> d; int res = query(c, d) - query(c, b - 1) - query(a - 1, d) + query(a - 1, b - 1); printf("%d\n", res); } } return 0; }