hdu 5465 Clarke and puzzle 树状数组

克拉克是一名人格分裂患者。某一天,有两个克拉克(aabb)在玩一个方格游戏。  
这个方格是一个nmnm的矩阵,每个格子里有一个数cijci,jaa想开挂,想知道如何打败bb。  
他们要玩qq次游戏,每一次做一次操作:  
1. 取出当中的一个子矩阵x1y1x2y2(x1,y1)(x2,y2)玩游戏。两个人轮流行动,每一次只能从这个子矩阵中的一个方格cijci,j中减掉一个的数d1dcijd(1dci,j),当一个格子的数为00时则不能减。如果操作完后另一者无法操作,那么胜利。否则失败。现在aa作为先手,想知道是否存在一种方案使得自己胜利。  
2. 将cijci,j的数改成bb  
输入描述
第一行一个整数T1T5T(1T5),表示数据的组数。  
每组数据第一行为三个整数nmq1nm5001q2105n,m,q(1n,m500,1q2105)。  
接下来是一个nnmm列的矩阵,其中第ii行第jj列的数为cij0cij109ci,j(0ci,j109)。  
接下来时qq行,第一个数为optopt。当opt1opt=1时,后面接着四个整数,依次表示x1y1x2y21x1x2n1y1y2mx1,y1,x2,y2(1x1x2n,1y1y2m),表示一个询问;当opt2opt=2时,后面接着三个整数xyz1xn1ym0z109x,y,z(1xn,1ym,0z109),表示将cxycx,y更改为zz
输出描述
对于每组数据,每个询问输出aa是否能胜利,如果能,输出YesYes,否则输出NoNo
输入样例
1
1 2 3
1 2
1 1 1 1 2
2 1 2 1
1 1 1 1 2
输出样例
Yes
No
Hint
第一个询问:一开始aa可以在12(1,2)的格子上减掉11,则接下来无论bb怎么选,都还剩一个11,所以aa胜利。
第二个询问:无论aa怎么选,都还剩下一个11,所以bb胜利。
题目要求二维的nim游戏,考虑到nim的结论是xor和为0则必败、否则必胜,那么我们只需要维护子矩阵的xor和。由于xor有前缀和性质,所以我们可以用一个二维bit来维护(1, 1)-(a, b)的矩阵的xor和,然后由sumx2y2 xor sumx2y11 xor sumx11y2 xor sumx11y11sum(x2,y2) xor sum(x2,y11) xor sum(x11,y2) xor sum(x11,y11)来得到答案即可。单点修改在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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值