克拉克是一名人格分裂患者。某一天,有两个克拉克(a和b)在玩一个方格游戏。 这个方格是一个n∗m的矩阵,每个格子里有一个数ci,j。 a想开挂,想知道如何打败b。 他们要玩q次游戏,每一次做一次操作: 1. 取出当中的一个子矩阵(x1,y1)−(x2,y2)玩游戏。两个人轮流行动,每一次只能从这个子矩阵中的一个方格ci,j中减掉一个的数d(1≤d≤ci,j),当一个格子的数为0时则不能减。如果操作完后另一者无法操作,那么胜利。否则失败。现在a作为先手,想知道是否存在一种方案使得自己胜利。 2. 将ci,j的数改成b
输入描述
第一行一个整数T(1≤T≤5),表示数据的组数。 每组数据第一行为三个整数n,m,q(1≤n,m≤500,1≤q≤2∗105)。 接下来是一个n行m列的矩阵,其中第i行第j列的数为ci,j(0≤ci,j≤109)。 接下来时q行,第一个数为opt。当opt=1时,后面接着四个整数,依次表示x1,y1,x2,y2(1≤x1≤x2≤n,1≤y1≤y2≤m),表示一个询问;当opt=2时,后面接着三个整数x,y,z(1≤x≤n,1≤y≤m,0≤z≤109),表示将cx,y更改为z。
输出描述
对于每组数据,每个询问输出a是否能胜利,如果能,输出Yes,否则输出No。
输入样例
1 1 2 3 1 2 1 1 1 1 2 2 1 2 1 1 1 1 1 2
输出样例
Yes No
Hint
第一个询问:一开始a可以在(1,2)的格子上减掉1,则接下来无论b怎么选,都还剩一个1,所以a胜利。 第二个询问:无论a怎么选,都还剩下一个1,所以b胜利。
题目要求二维的nim游戏,考虑到nim的结论是xor和为0则必败、否则必胜,那么我们只需要维护子矩阵的xor和。由于xor有前缀和性质,所以我们可以用一个二维bit来维护(1, 1)-(a, b)的矩阵的xor和,然后由sum(x2,y2) xor sum(x2,y1−1) xor sum(x1−1,y2) xor sum(x1−1,y1−1)来得到答案即可。单点修改在bit上是很容易的。
#include <cstdio> #include <cstring> using namespace std; int T, n, m, q; int c[600][600]; int num[600][600]; int lowbit(int x) { return x & (-x); } int query(int x, int y) { int ret = 0; for (int i = x; i > 0; i -= lowbit(i)) { for (int j = y; j > 0; j -= lowbit(j)) { ret ^= c[i][j]; } } return ret; } void modify(int x, int y, int delta) { for (int i = x; i <= n; i += lowbit(i)) { for (int j = y; j <= m; j += lowbit(j)) { c[i][j] ^= delta; } } } int main() { scanf("%d", &T); while(T--) { scanf("%d%d%d", &n, &m, &q); memset(num, 0, sizeof(num)); memset(c, 0, sizeof(c)); for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { scanf("%d", &num[i][j]); modify(i, j, num[i][j]); } int opt, x1, y1, x2, y2; int x, y, z; for (int i = 1; i <= q; i++) { scanf("%d", &opt); if (opt == 1) { scanf("%d%d%d%d", &x1, &y1, &x2, &y2); int ans = query(x2, y2) ^ query(x1 - 1, y1 - 1) ^ query(x2, y1 - 1) ^ query(x1 - 1, y2); //printf("<%d>\n", ans); if (ans == 0) printf("No\n"); else printf("Yes\n"); } else { scanf("%d%d%d", &x, &y, &z); modify(x, y, z ^ num[x][y]); num[x][y] = z; } } } return 0; }