题目
一个(n imes n)的零矩阵(a),(m)次操作:
- (x_0 y_0 x_1 y_1 c),对(a[x][y],xin[x_0,x_1],yin[y_0,y_1])异或上一个数(c)
- (x_0 y_0 x_1 y_1),询问这个子矩阵的异或和
(nle 1000,mle 10^5,0le c<2^{62})。
分析
树状数组的妙用!关键还是在异或的性质和处理矩阵的方法。
首先考虑矩阵异或,单点查询,那么对于一个修改((x_0,y_0,x_1,y_1,c)),如果我们对((x_0,y_0),(x_0,y_1+1),(x_1+1,y_0),(x_1+1,y_1+1))都异或上(c),那么一个结论就是一个点((x,y))的值就是((1,1,x,y))的异或和。(画一画图就知道了)
现在是询问子矩阵的异或和,显然首先转化为求任意的((1,1,x,y))的异或和。
在之前那种修改方式下,一个点的点值就是((1,1,x,y))的异或和,那么要求((1,1,x,y))的异或和,我们就把这些异或和异或起来。
考虑每个点的出现次数,我们会发现,对于一个点((x_0,y_0)),它在总异或中出现奇数次当且仅当((x-x_0+1)(y-y_0+1))为奇数。
例如我们统计((1,1,3,4))的异或和的异或和,我们只用统计((1,2),(1,4),(3,2),(3,4))这几个点的值。
于是对于每一种((x,y))的奇偶情况,我们开一个二维树状数组来进行单点修改,前缀异或和查询即可(二维树状数组就是一维树状数组的每个点都是一个树状数组)。
代码
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long giant;
int read() {
int x=0,f=1;
char c=getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
for (;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*f;
}
const int maxn=1e3+2;
int n,m;
inline int lowbit(int x) {return x&-x;}
struct BIT {
giant c[maxn][maxn];
void change(int x,int y,giant d) {
for (;x<=n+1;x+=lowbit(x)) for (int i=y;i<=n+1;i+=lowbit(i)) c[x][i]^=d;
}
giant query(int x,int y) {
giant ret=0;
for (;x;x-=lowbit(x)) for (int i=y;i;i-=lowbit(i)) ret^=c[x][i];
return ret;
}
} t[2][2];
giant get(int x,int y) {
giant ret=t[x&1][y&1].query((x+1)>>1,(y+1)>>1);
}
giant get(int x1,int y1,int x2,int y2) {
giant a=get(x1-1,y1-1);
giant b=get(x1-1,y2);
giant c=get(x2,y1-1);
giant d=get(x2,y2);
giant ret=a^b^c^d;
return ret;
}
void change(int x,int y,giant c) {
t[x&1][y&1].change((x+1)>>1,(y+1)>>1,c);
}
void change(int x1,int y1,int x2,int y2,giant c) {
change(x1,y1,c);
change(x1,y2+1,c);
change(x2+1,y1,c);
change(x2+1,y2+1,c);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
#endif
n=read(),m=read();
while (m--) {
int o=read(),x1=read(),y1=read(),x2=read(),y2=read();
giant c;
if (o==2) scanf("%lld",&c);
if (o==1) printf("%lld
",get(x1,y1,x2,y2)); else change(x1,y1,x2,y2,c);
}
return 0;
}