异或和问题
现在有一个n行n列的矩阵。初始状态下,矩阵里的所有值都为0。行的编码是从1到n,列的编码也同样是从1到n.
ai,?j.是在第i行第j列的数。子矩阵(x0, y0, x1, y1)是由ai,?j. (x0?≤?i?≤?x1, y0?≤?j?≤?y1)组成的矩阵。
现在需要进行下列的两个操作:
1.查询(x0,?y0,?x1,?y1):输出子矩阵(x0,?y0,?x1,?y1)的异或和。
2.更新(x0, y0, x1, y1, v):将子矩阵(x0,?y0,?x1,?y1)中的每一个数都异或上一个v
第一行包含两个数:n(1<=n<=1000),m(1<=m<=100000) m是需要操作的次数。
对于接下来的m行:
如果该行的第一个数是1,表示查询操作,后面是x0, y0, x1, y1.
如果该行的第一个数是2,表示更新操作,后面是x0, y0, x1, y1, v.
数据保证:1?≤?x0?≤?x1?≤?n, 1?≤?y0?≤?y1?≤?n ,0?≤?v?<?262
对于每一个查询操作,输出对应的结果,占单独的一行。
2 3 2 1 1 2 2 1 2 1 1 1 1 2 1 1 1 2 2
2 Note: 经过第一个操作得到 1 1 1 1 经过第二个操作得到 3 1 1 1 询问:3XOR1XOR1XOR1=2
分析:首先,本题进行的是区间更新,以及区间查询的操作。
根据异或具有的性质,奇数个a相异或等于a , 偶数个a相异或等于0,
如果将 (x0,?y0,?x1,?y1)区间异或一个v,那么对后面求区间异或和的影响,无非是两种,等于更新前的异或和,
或者在原本的异或和基础上异或一个v.所以可以考虑将影响直接作用于单点上,进而转变为单点更新。
如果是在一个一维的区间里进行操作的话,比如现在有数组b1,b2,b3,b4,b5....bn
对于区间[2,n]异或上一个v的话,再查询区间[1,m].
如果m为奇数,也就是与更新时的左端点奇偶性不同,
在异或过程,就会不断的互相抵消,最后为一开始的b1^b2^b3..^bm.
如果m为偶数,也就是与更新时的左端点奇偶性相同,就将上述结果再异或上v;
所以处理的时候,就可以当作单点更新,可以把左端点b2分为两种状态,
在m为奇数查询下的状态和在m为偶数查询下的状态,分别为b2和b2^v;
同理,如果在区间[a,b]上进行更新操作,相当于进行两个操作,将[a,n]异或一次,再将[b+1,n]异或一次
如果在区间[a,b]上进行查询操作,相当于[1,b]^[1,a-1]
二维的情况下,也是相同的道理,只是要区分两个坐标的奇偶性,所以用到4个状态来储存。
现在求解就变成了 单点更新和区间查询,就可以直接使用二维树状数组了.
这个将区间更新转为单点更新的操作,类似于差分树状数组对加减法的区间操作。
代码如下:
#include <cstdio> #include <iostream> #include <algorithm> using namespace std; typedef long long LL; const int MAXN=1010; LL c[2][2][MAXN][MAXN]; int n,m; LL val; int lowbit(int x) { return x&-x; }
void add(int a,int b) { for(int i=a;i<=n;i+=lowbit(i)) for(int j=b;j<=n;j+=lowbit(j)) c[a&1][b&1][i][j]^=val; } LL sum(int a,int b) { int aa=a&1,bb=b&1; LL ans=0; for(int i=a;i>=1;i-=lowbit(i)) for(int j=b;j>=1;j-=lowbit(j)) { ans^=c[a&1][b&1][i][j]; } return ans; }
int main() { scanf("%d%d",&n,&m); int x1,x2,y1,y2,op; LL ans;
for(int i=1;i<=m;i++) { scanf("%d",&op);
if(op==1) { scanf("%d%d%d%d",&x1,&y1,&x2,&y2); ans=sum(x2,y2)^sum(x2,y1-1)^sum(x1-1,y2)^sum(x1-1,y1-1); printf("%I64d\n",ans); }
else { scanf("%d%d%d%d%I64d",&x1,&y1,&x2,&y2,&val); add(x1,y1),add(x2+1,y1),add(x1,y2+1),add(x2+1,y2+1); }
} return 0; }