zoukankan      html  css  js  c++  java
  • 2018年四校联合周赛-第二场 B.异或和问题(二维树状数组)

    异或和问题

    TimeLimit:1000MS  MemoryLimit:256MB
    64-bit integer IO format:%I64d
    Problem Description

    现在有一个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

    Input

    第一行包含两个数: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   

    Output

    对于每一个查询操作,输出对应的结果,占单独的一行。

    SampleInput
    2 3 
    2 1 1 2 2 1
    2 1 1 1 1 2
    1 1 1 2 2
    SampleOutput
    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 ",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; }


  • 相关阅读:
    linux清理内存
    华为代码注释标准
    Spring缓存机制的理解
    jQuery实现动态分割div—通过拖动分隔栏实现上下、左右动态改变左右、上下两个相邻div的大小
    模仿iframe框架,由分隔栏动态改变左右两侧div大小———基于jQuery
    js实现由分隔栏决定两侧div的大小—js动态分割div
    java基于socket的简单聊天系统
    中国移动归属地区号大全
    将本地光盘做成yum源
    windows下设置计划任务自动执行PHP脚本
  • 原文地址:https://www.cnblogs.com/a249189046/p/8598229.html
Copyright © 2011-2022 走看看