HDU5892Resident Evil(二维树状数组+状态压缩)

来源:互联网 发布:java redis 获取list 编辑:程序博客网 时间:2024/06/06 18:29

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5892

题意:给定n和m表示有一个n*n的矩阵和m个操作,操作1:给出左上角的位置[x1,y1]和右下角的位置[x2,y2],然后给定k表示有k对[a,b]接下来在这个给定的矩阵中每个格子中都添加b个a类物品;操作2:给定左上角位置[x1,y1]和右下角位置[x2,y2],求所有物品在这个矩阵中的奇偶情况。


分析:
网上有很多千篇一律的题解,对于树状数组的使用都没有说的点上。
首先提醒一下,树状数组就相当于一个前缀和数组。

考虑一维情况,给一段区间[L,R]^X,然后询问一段区间内物品的奇偶性。
对于树状数组C[L..R],每个位置异或X的次数是奇、偶交错的,而异或相同的一个数偶数次等于没异或,异或相同的一个数奇数次相当于异或一次。为什么疑惑X的次数是奇、偶交错的?因为树状数组是前缀和数组,如果在区间[1, 4]内加了一个奇数x,那么普通的前缀和数组sum[1]就会加一次x,sum[2]就会加两次x,sum[3]加三次x,sum[4]加四次x。可以发现是奇、偶交错的。

第一个操作涉及二维数组的区间更新,就是说我们要把对应矩阵区域内的所有格子加上对应数量的怪物。
由于我们不需要知道对应的怪物的具体数量,只需要知道对应的怪物的数量是奇数还是偶数。所以我们
这里可以用0,1来表示对应怪物的数量是偶数还是奇数,并且我们要用到位运算异或。
在一个矩形区域内,我们要加入偶数个对应的物品,那么,无论这个矩形区域的面积是偶数还是奇数,其整个区域
内加入某种物品的个数肯定为偶数,所以是不会影响整个前缀和数组的,对于单个位置也是,因为是偶数个品,
也不会影响前缀和数组。所以对应的异或值是不会改变的。

对于加入奇数个物品的情况,由上述的奇偶交错理论,可以发现对于前缀和数组,加偶数个就是没有改变,所以
对于前缀和数组,只有x+1,x+3,x+5...等格子是毫无影响的,只有被添加到x+2,x+4,x+6...等格子,该格子的值才会变化。也就是说与前缀和数组的某个下标同奇偶的下标对应得值才会改变,不同的就不会改变。对于y的情况也是一样。再讨论下,加入奇数个物品时,加入的矩阵面积为奇数和偶数的情况,如果为奇数,那么矩矩形区域内加入的物品总数就是奇数个,对于后面的前缀和数组是有影响的,如果是偶数,那么矩阵区域内加入的物品总数就是偶数个,对于后面的前缀和数组是没有什么影响的。

实际上,加入奇数个物品,在实现上还是很难想到的。对于一个一维的树状数组,如果在[4,6]这个区间内,每个
点都加入了奇数个物品,那么这个区间内加入的物品总数也是奇数个,因为(6-4+1)*奇数 = 奇数。在进行寄偶交错的更新时,下标为4、6、8....的前缀和数组的异或值都被更改了。但因为加入的物品总是是奇数个,所以对
于下标为7、9、11的前缀和数组的异或值也需要更改。再举个例子,如果在[4,7]这个区间内,每个点都加入奇数个物品,那么这个区间内加入的物品总数是偶数个,因为(7-4+1)*奇数 = 偶数。奇偶交错的更改与上面的一个例子一样,但是因为区间内加入的物品总数也是偶数个,实际上下标为8、10、12的前缀和数组的异或值原本是不需要更改的。所以又得改回去。这边得结合代码来理解。其实这也是只对应这道题的。总之,最重要的一点就是树状数组是个前缀和数组,一切都得往这去想。
因为有50个物品,所以需要状态压缩一下。
#include<iostream>#include<algorithm>#include<cstring>#include<cstdio>using namespace std;typedef long long ll;int n, m;ll tree[2][2][3005][3005];int x1, y1, x2, y2;ll mon[55];void add(int x, int y, ll v){    for(int i = x; i <= n; i += (i&-i))        for(int j = y; j <= n; j += (j&-j))            tree[x&1][y&1][i][j] ^= v;}ll query(int x, int y){    ll res = 0;    for(int i = x; i; i -= (i&-i))        for(int j = y; j; j -= (j&-j))            res ^= tree[x&1][y&1][i][j];    return res;}int main(){    char op;    int x1, y1, x2, y2, k, x, y;    while(scanf("%d%d", &n, &m) != EOF)    {        memset(tree, 0, sizeof(tree));        while(m--)        {            scanf(" %c%d%d%d%d", &op, &x1, &y1, &x2, &y2);            if(op == 'P')            {                memset(mon, 0, sizeof(mon));                scanf("%d", &k);                while(k--)                {                    scanf("%d%d", &x, &y);                    --x;                    mon[x] += y;                }                ll temp = 0;                for(int i = 0; i < 50; i++)                    if(mon[i] & 1)                        temp |= (1ll<<i);                ++x2; ++y2;                add(x1, y1, temp);                add(x2, y2, temp);                add(x1, y2, temp);                add(x2, y1, temp);            }            else            {                --x1;--y1;                ll temp = query(x1, y1)^query(x2, y2)^query(x1, y2)^query(x2, y1);                for(int i = 0; i < 50; i++)                    if(temp & (1ll<<i))                        printf("2 ");                    else                        printf("1 ");                    puts("");            }        }    }    return 0;}


原创粉丝点击