牛客小白月赛9-红球进黑洞(异或线段树)

故事扮演 提交于 2019-12-01 05:04:59

题意:

操作\(1\):求\([l,r]\)区间和。
操作\(2\):区间\([l,r]\)的数异或上\(k\)

分析:

对区间进行位运算是没啥公式的,所以要考虑对数的每一位建线段树,记录每一位\(1\)出现的次数。询问的时候求出每一位的贡献即可。
线段树维护的是每一位\(1\)的出现次数。
区间异或:首先如果\(k\)\(i\)位为\(0\),则异或值不变,否则异或\(i\)位的区间\([l,r]\)相当于将这个区间的\(0\)变成\(1\)\(1\)变成\(0\)
区间或:或上某一位1才有意义
区间与:与上某一位0才有意义

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

const int N = 1e5 + 5;
const int mod = 1e9 + 7;

int n, q, w[N];
int seg[N << 2][19], lazy[N << 2][19];
int opt, res, x, y, k;
LL ans, base;

void build(int rt, int l, int r, int o) {
    if (l == r) {
        seg[rt][o] += ((w[l] >> o) & 1);
        return ;
    }
    int mid = l + r >> 1;
    build(rt << 1, l, mid, o);
    build(rt << 1 | 1, mid + 1, r, o);
    seg[rt][o] = seg[rt << 1][o] + seg[rt << 1 | 1][o];
}

void pushdown(int rt, int l, int r, int mid, int o) {
    if (lazy[rt][o]) {
        lazy[rt << 1][o] ^= 1;
        lazy[rt << 1 | 1][o] ^= 1;
        seg[rt << 1][o] = (mid - l + 1) - seg[rt << 1][o];
        seg[rt << 1 | 1][o] = (r - mid) - seg[rt << 1 | 1][o];
        lazy[rt][o] ^= 1;
    }
}

void update(int rt, int l, int r, int ql, int qr, int o) {
    if (l >= ql && r <= qr) {
        seg[rt][o] = (r - l + 1) - seg[rt][o];
        lazy[rt][o] ^= 1;
        return ; 
    }
    int mid = l + r >> 1;
    pushdown(rt, l, r, mid, o);
    if (ql <= mid) update(rt << 1, l, mid, ql, qr, o);
    if (qr > mid) update(rt << 1 | 1, mid + 1, r, ql, qr, o);
    seg[rt][o] = seg[rt << 1][o] + seg[rt << 1 | 1][o];
}

int query(int rt, int l, int r, int ql, int qr, int o) {
    if (l >= ql && r <= qr) {
        return seg[rt][o];
    }
    int mid = l + r >> 1, ans = 0;
    pushdown(rt, l, r, mid, o);
    if (ql <= mid) ans += query(rt << 1, l, mid, ql, qr, o);
    if (qr > mid) ans += query(rt << 1 | 1, mid + 1, r, ql, qr, o);
    return ans;
}

int main() {
    scanf("%d %d", &n, &q);
    for (int i = 1; i <= n; i++) scanf("%d", w + i);
    for (int i = 0; i <= 17; i++) build(1, 1, n, i);
    while (q--) {
        scanf("%d", &opt);
        if (opt == 1) {
            scanf("%d %d", &x, &y);
            ans = 0, base = 1;
            for (int i = 0; i <= 17; i++) {
                ans += 1LL * base * query(1, 1, n, x, y, i);
                base <<= 1;
            }
            printf("%lld\n", ans);
        } else {
            scanf("%d %d %d", &x, &y, &k);
            for (int i = 0; i <= 17; i++) {
                res = ((k >> i) & 1);
                if (res) update(1, 1, n, x, y, i);
            }
        }
    }
    return 0;
}

```

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!