二维树状数组总结

概念

维护原二维数列的差分数列,从而用二维树状数组进行单点修改,求二维前缀和等操作,进而转化为区域加,区域求和等操作。

例题

[Luogu] P4514 上帝造题的七分钟

\(Link\)

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

\(Link\)

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;
}
posted @ 2020-11-18 17:13  andysj  阅读(203)  评论(0编辑  收藏  举报