SMOJ 1980 XOR (线段树)

来源:互联网 发布:淘宝哪里可以买二手货 编辑:程序博客网 时间:2024/05/29 17:11

这里写图片描述
这里写图片描述
这里写图片描述


Solution

这就是棵裸的线段树,我一下就切掉了。话说最近考了好多好多道线duang树啊!感觉我自己都被duang掉了。

由于异或不满足分配律,不能像矩阵那题一样直接对和进行异或操作。而异或是满足结合律的,于是我们可以对于多个异或标记进行合并。

我们抓住异或的本质(别说抓不住),异或x就是如果x二进制的某一位是1,就将被异或的那个数的那一位1变成0,0变成1。我们就在线段树的叶子开一个大小为20左右的数组,记录二进制下每一位是0是1。然后向上维护每一位的和。我们知道一段区间的1个个数就是和,0的个数就是区间长度减去和。我们对这段区间异或x,将x分解,有一位是1,代表将这段区间的每一个数的1变0,0变1,再求和。其实和就变成了区间长度减去和了。于是和就可以进行区间信息加法了。

然后将懒惰标记lazy合并后向下传,和也照样维护,到底端就变成了那一位是0是1了。查询的时候再将对应的位的权乘和累加起来,然后这题就这样的过了。

时间的话,多一个20倍的常数,然而题目良心,操作不多,所以不虚。

ps:这题的数据有毒??我暴力跟正解拍了半天没出错,正解一交AC了,暴力却只有20分,WA了一堆,真是让我长见识了!另外有人的暴力拿了50分,一问,答曰:我的线段树的标记是down到底的,太强大了!我震惊得说不出话来,此处省略rand()+1字。


代码

#include <cstdio>#include <iostream>#include <cstring>#include <cmath>#include <cstdlib>#include <algorithm>#define maxn 100010using namespace std;typedef long long LL;int n, m, a[maxn];struct Tnode{    int num[22];    int lazy;}tree[maxn<<2];void build(int root, int L, int R){    if(L == R){        int temp = a[L], t = 0;        while(temp){            tree[root].num[t] = temp & 1;            t ++;            temp >>= 1;        }        tree[root].lazy = 0;        return;    }    int mid = (L + R) >> 1, Lson = root << 1, Rson = root << 1 | 1;    build(Lson, L, mid);    build(Rson, mid+1, R);    for(int i = 0; i <= 20; i++)          tree[root].num[i] = tree[Lson].num[i] + tree[Rson].num[i];    tree[root].lazy = 0;}void Down(int root, int L, int R){    if(!tree[root].lazy)  return;    int mid = (L + R) >> 1, Lson = root << 1, Rson = root << 1 | 1;    int temp = tree[root].lazy, t = 0;    while(temp){        if(temp & 1){            tree[Lson].num[t] = (mid - L + 1) - tree[Lson].num[t];            tree[Rson].num[t] = (R - mid) - tree[Rson].num[t];        }        t ++;        temp >>= 1;    }    tree[Lson].lazy ^= tree[root].lazy;    tree[Rson].lazy ^= tree[root].lazy;    tree[root].lazy = 0;}void update(int root, int L, int R, int x, int y, int v){    if(x > R || y < L)  return;    if(x <= L && y >= R){        int temp = v, t = 0;        while(temp){            if(temp & 1)  tree[root].num[t] = (R - L + 1) - tree[root].num[t];            t ++;            temp >>= 1;        }        tree[root].lazy ^= v;        return;    }    Down(root, L, R);    int mid = (L + R) >> 1, Lson = root << 1, Rson = root << 1 | 1;    update(Lson, L, mid, x, y, v);    update(Rson, mid+1, R, x, y, v);    for(int i = 0; i <= 20; i++)          tree[root].num[i] = tree[Lson].num[i] + tree[Rson].num[i];}LL query(int root, int L, int R, int x, int y){    if(x > R || y < L)  return 0LL;    if(x <= L && y >= R){        LL ans = 0LL;        for(int i = 0; i <= 20; i++)  ans += (1LL << i) * tree[root].num[i];        return ans;    }    Down(root, L, R);    int mid = (L + R) >> 1, Lson = root << 1, Rson = root << 1 | 1;    LL temp1 = query(Lson, L, mid, x, y);    LL temp2 = query(Rson, mid+1, R, x, y);    return temp1 + temp2;}int main(){    scanf("%d", &n);    for(int i = 1; i <= n; i++)  scanf("%d", &a[i]);    build(1, 1, n);    scanf("%d", &m);    int t, l, r, x;    for(int i = 1; i <= m; i++){        scanf("%d%d%d", &t, &l, &r);        if(t == 1)            printf("%lld\n", query(1, 1, n, l, r));        else{            scanf("%d", &x);            update(1, 1, n, l, r, x);        }    }    return 0;}

这里写图片描述