1. 程式人生 > 實用技巧 >二維樹狀陣列總結

二維樹狀陣列總結

概念

維護原二維數列的差分數列,從而用二維樹狀陣列進行單點修改,求二維字首和等操作。

例題

[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}\)

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[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;
}