二維樹狀數組-POJ 2155 Matrix

樹狀數組

  • 什麼是樹狀數組?
    簡單來說,就是暴力遍歷數組來解決區間問題等,不過遍歷的路徑使用了位運算來進行壓縮,複雜度是O(log2(n))這樣就不會超時了(爲所欲爲?)。

  • lowbit()操作
    其核心是神奇的lowbit操作,lowbit(x)=x&(-x),它的功能是找到x的二進制數的最後一個1,原理是利用負數的補碼錶示,補碼是原碼取反加一。例如x=6=00000110(2),-x=x補=11111010(2),那麼lowbit(x)=x&(-x)=10(2)=2。
    從lowbit()引出數組a[],a[x]的值是把ax(題目輸入初始值)和他前面的m個數相加,如下表所示:
    在這裏插入圖片描述
    圖形化:
    在這裏插入圖片描述
    那麼通過數組a[],就可以求sum,例如sum(8)=a[8],sum(7)=a[7]+a[6]+a[4],sum(6)=a[6]+a[4],如此一來不就是我們要的路徑壓縮了嗎?
    同樣地,更新ax時也要更新a[],例如更新a3,那麼首先更改a[3];然後3+lowbit(3)=4,更改a[4];接着4+lowbit(4)=8,更改a[8]。

  • 二維樹狀數組
    相應的,我們可以推導出二維樹狀數組,例如更新點求區間輸入座標(x,y),(u,v)求sum黃色區域,如下圖所示:
    在這裏插入圖片描述
    由圖易得所求區間黃色區域爲sum(u,v)-sum(x-1,v)-sum(u,y-1)+sum(x-1,y-1)

例題

傳送門: POJ-2155

Given an N*N matrix A, whose elements are either 0 or 1. A[i, j] means the number in the i-th row and j-th column. Initially we have A[i, j] = 0 (1 <= i, j <= N).
We can change the matrix in the following way. Given a rectangle whose upper-left corner is (x1, y1) and lower-right corner is (x2, y2), we change all the elements in the rectangle by using “not” operation (if it is a ‘0’ then change it into ‘1’ otherwise change it into ‘0’). To maintain the information of the matrix, you are asked to write a program to receive and execute two kinds of instructions.
1 C x1 y1 x2 y2 (1 <= x1 <= x2 <= n, 1 <= y1 <= y2 <= n) changes the matrix by using the rectangle whose upper-left corner is (x1, y1) and lower-right corner is (x2, y2).
2.Q x y (1 <= x, y <= n) querys A[x, y].

input:

The first line of the input is an integer X (X <= 10) representing the number of test cases. The following X blocks each represents a test case.

output:

For each querying output one line, which has an integer representing A[x, y].
There is a blank line between every two continuous test cases.

Sample Input:

1
2 10
C 2 1 2 2
Q 2 2
C 2 1 2 1
Q 1 1
C 1 1 2 1
C 1 2 1 2
C 1 1 2 2
Q 1 1
C 1 1 2 1
Q 2 1

Sample Output:

1
0
0
1

題意

在n*n矩陣中每次對一個子矩陣進行翻轉(0變1,1變0),然後多次詢問某個點是0還是1。

分析

更新區間求點,用二維樹狀數組解決,每次更新子矩陣+1,最後求點%2就得到結果。(其實就是二維數組模擬的思路,壓縮路徑罷了)。

代碼

#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn = 1003;
char s[5];
int a[maxn][maxn], n;
int lowbit(int x) {
	return (x & (-x));
}
void update(int x, int y) {
	for (int i = x; i <= n; i += lowbit(i)) {
		for (int j = y; j <= n; j += lowbit(j)) {
			a[i][j]++;
		}
	}
}
int sum(int x, int y) {
	int res = 0;
	for (int i = x; i > 0; i -= lowbit(i)) {
		for (int j = y; j > 0; j -= lowbit(j))
			res += a[i][j];
	}
	return res;
}
int main() {
	int ca, t;
	int x, y, u, v;
	scanf("%d", &ca);
	while (ca--) {
		memset(a, 0, sizeof(a));
		scanf("%d%d", &n, &t);
		while (t--) {
			scanf("%s%d%d", s, &x, &y);
			if (s[0] == 'C') {
				scanf("%d%d", &u, &v);
				x++, y++;
				u++, v++;
				update(u, v);
				update(x - 1, y - 1);
				update(x - 1, v);
				update(u, y - 1);
			}
			else {
				printf("%d\n", sum(x, y) % 2);
			}
		}
		printf("\n");
	}
	return 0;
}

小結

  1. 樹狀數組能解決的題都能用線段樹解決,但是樹狀數組編程複雜度低,完成效率更高!(大佬請無視)

你的點贊將會是我最大的動力

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章