链接
题意
给出一个n*n的矩阵,初始值均为0,然后m组操作,可以给出一个矩形区域,使其中元素都异或某值,也可以给出矩形区域,查询其中所有元素的异或值。
题解
二维树状数组的经典题目,跟异或操作结合。
二维树状数组的基本功能是单点更新和区间查询,当然只能查询某点到(1, 1)点的值,也可以利用差分的思想改成区间更改和单点查询。这里是异或操作,事实上跟加减法的运算规律类似,也是可叠加的。
考虑一维树状数组的区间异或和单点查询(注意这里不是题意中要求的区间查询),想对一个区间进行异或,第一反应是对区间端点进行add操作(就想利用差分对加减法所做的那样),查询的时候get区间即可,看似是对的。但是异或和加减法不同,加减法是完全的叠加,但是异或却是奇数个元素叠加,偶数个元素消除的。所以我们在记录区间内的异或操作时,需要按照元素的奇偶进行分类,比如我们利用add在区间的x位置放置了一个异或元素,如果x是奇数,那么它能影响到的只有[x, n]之间同为奇数位置的元素,对偶数位置的元素是没有影响的,因为消除了。我们只要建两个树状数组分别记录奇偶两种元素下标提供的异或值即可,因为是互相不影响的。但是我们现在要求的是区间查询,事实上对异或操作而言,单点查询x就是[1, x]的区间查询,因为x之前的影响两两消除了。
推广到2维情况也一样,不过这时候需要对矩阵中的元素个数按奇偶分类,就需要建4个树状数组了。
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long lint;
#define maxn (1010)
inline lint lowbit(lint x) { return x&-x; }
lint c[2][2][maxn][maxn];
void add(int x, int y, int val, int n)
{
for(int i = x; i <= n; i += lowbit(i))
{
for(int j = y; j <= n; j += lowbit(j))
c[x&1][y&1][i][j] ^= val;
}
}
lint get(int x, int y)
{
lint o = 0;
for(int i = x; i; i -= lowbit(i))
{
for(int j = y; j; j -= lowbit(j))
o ^= c[x&1][y&1][i][j];
}
return o;
}
int main()
{
int n, m;
cin >> n >> m;
int op, x1, x2, y1, y2;
lint val;
while(m--)
{
scanf("%d%d%d%d%d", &op, &x1, &y1, &x2, &y2);
if(op == 1)
{
val = 0;
val ^= get(x2, y2);
val ^= get(x1 - 1, y2);
val ^= get(x2, y1 - 1);
val ^= get(x1 - 1, y1 - 1);
cout << val << endl;
}
else
{
cin >> val;
add(x1, y1, val, n);
add(x1, y2 + 1, val, n);
add(x2 + 1, y1, val, n);
add(x2 + 1, y2 + 1, val, n);
}
}
return 0;
}