主要思路
首先看到这个题可以考虑树套树,然后就做完了。
我们考虑用二维树状数组进行差分。当然,如果按正常的想法差分的话,我们只能想到用前缀和维护单点值,而不是维护区间异或和。我们设差分后的结果:
\[ d_{i, j} = a_{i, j} \oplus a_{i – 1, j} \oplus a_{i, j – 1} \oplus a_{i – 1, j – 1} \]
这个差分的方式有点不太一样,因为我们比传统差分多算了 \(a_{i – 1, j – 1}\) 的贡献。
每当我们进行区域操作时,通过对二维差分的联想,我们大概知道操作 \(d_{x_1, y_1}, d_{x_2 + 1, y_1}, d_{x_1, y_2 + 1}, d_{x_2 + 1, y_2 + 1}\) 可以达到目的。那么我们思考一下,这个二维差分做完之后,我们如何做区间异或和。这里,我们用 \(\sum\) 代表区间异或和的结果:
\[ ans = \sum_{i = 1}^{x} \sum_{j = 1}^{y} a_{i, j} \]
强行带入 \(d_{i, j}\) 的定义式:
\[ ans = \sum_{i = x_1}^{x_2} \sum_{j = y_1}^{y_2} d_{i, j} \oplus a_{i – 1, j} \oplus a_{i, j – 1} \oplus a_{i – 1, j – 1} \]
最终变成了:
\[ ans = \sum_{i = 2k + (x \bmod 2)}^x \sum_{j = 2k + (y \bmod 2)}^y d_{i, j} \]
所以,在这个地方直接做差分是有意义的。我们可以对 \((x, y)\) 的奇偶性开 4 个二维树状数组,然后对奇偶性做即可。
代码
// CF341D.cpp
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX_N = 1010;
int n, q;
struct BIT
{
inline int lowbit(int x) { return x & (-x); }
ll nodes[MAX_N][MAX_N];
void update(int x, int y, ll d)
{
for (int i = x; i <= n; i += lowbit(i))
for (int j = y; j <= n; j += lowbit(j))
nodes[i][j] ^= d;
}
ll query(int x, int y)
{
ll ret = 0;
for (int i = x; i >= 1; i -= lowbit(i))
for (int j = y; j >= 1; j -= lowbit(j))
ret ^= nodes[i][j];
return ret;
}
} tr[4];
int getParity(int x, int y) { return (x & 1) | ((y & 1) << 1); }
void modify(int x, int y, ll d) { tr[getParity(x, y)].update(x, y, d); }
ll query(int x, int y) { return tr[getParity(x, y)].query(x, y); }
int main()
{
scanf("%d%d", &n, &q);
while (q--)
{
int opt, x1, y1, x2, y2;
ll d;
scanf("%d%d%d%d%d", &opt, &x1, &y1, &x2, &y2);
if (opt == 1)
{
ll ans = 0;
ans ^= query(x2, y2);
ans ^= query(x1 - 1, y2);
ans ^= query(x2, y1 - 1);
ans ^= query(x1 - 1, y1 - 1);
printf("%lld\n", ans);
}
else
{
scanf("%lld", &d), modify(x1, y1, d);
modify(x2 + 1, y1, d), modify(x1, y2 + 1, d);
modify(x2 + 1, y2 + 1, d);
}
}
return 0;
}