二维树状数组总结
概念
维护原二维数列的差分数列,从而用二维树状数组进行单点修改,求二维前缀和等操作,进而转化为区域加,区域求和等操作。
例题
[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}\)
复杂度\(\mathcal{O}(nm\log n\log m)\)。
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[1050][1050][5];
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 <= n; 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 & 1), 2); add(x, y, d * (y & 1), 3); add(x, y, d * (x & 1) * (y & 1), 4);
return;
}
int query(int x, int y)
{
return ((x + 1) & 1) * ((y + 1) & 1) * ask(x, y, 1) ^ ((y + 1) & 1) * ask(x, y, 2) ^ ((x + 1) & 1) * ask(x, y, 3) ^ ask(x, y, 4);
}
int main()
{
n = read(); m = read();
while (m -- )
{
int opt, a, b, c, d, x;
opt = read();
if (opt == 2)
{
a = read(); b = read(); c = read(); d = read(); x = read();
modify(a, b, x); modify(a, d + 1, x); modify(c + 1, b, x); modify(c + 1, d + 1, x);
}
else
{
a = read(); b = read(); c = read(); d = read();
int res = query(c, d) ^ query(c, b - 1) ^ query(a - 1, d) ^ query(a - 1, b - 1);
printf("%d\n", res);
}
}
return 0;
}