洛谷CF242E XOR On Segment

题目链接

  因为我们对区间进行异或的操作,如果我们考虑一个一个暴力的异或过去那肯定是会超时的。那么我就可以将二进制拆位,我们可以发现它的数据范围最大的是\(10^{6}\)也就是\(2^{19}\)次方左右的,我们可以把这每一位的二进制分别用一个线段树存下来,这样的话区间的异或操作就可以转化成所有数每一位的区间异或操作。这有什么区别呢,如果我们不拆位的话,很难实现区间的整体异或,但是拆位之后这一位上面就之后\(0,1\)这两个数字了,这一位上有多少个一就变成了区间求和的操作,异或操作就是求出来相反的数字的个数,可以用\(val = r - l + 1 - val\)来实现异或的操作。最后查询答案的时候就是把这\(20\)棵树都循环一遍最后将每一位都求和就好了。

#include <bits/stdc++.h>

using i64 = long long;

#define rep(i, a, n) for (int i = a; i < n; i ++ )
#define per(i, a, n) for (int i = n - 1; i >= a; i -- )
#define SZ(a) (int(a.size()))
#define pb push_back
#define all(a) a.begin(), a.end()
//head

constexpr int N = 100010;

int a[N], digit[20];
i64 val[N << 2][20], tag[N << 2][20];

void pushup(int u, int p) {
    val[u][p] = val[u << 1][p] + val[u << 1 | 1][p];
}

void pull(int u, int l, int r, int p) {
    val[u][p] = r - l + 1 - val[u][p];
    tag[u][p] ^= 1;
}

void pushdown(int u, int l, int r, int p) {
    if (tag[u][p]) {
        int mid = l + r >> 1;
        pull(u << 1, l, mid, p);
        pull(u << 1 | 1, mid + 1, r, p);
        tag[u][p] = 0;
    }
}

void build(int u, int l, int r, int p) {
    if (l == r) {
        val[u][p] = (a[l] & digit[p]) != 0;
        return ;
    }

    int mid = l + r >> 1;
    build(u << 1, l, mid, p), build(u << 1 | 1, mid + 1, r, p);
    pushup(u, p);
}

void modify(int u, int l, int r, int ln, int rn, int p) {
    if (ln <= l && r <= rn) {
        pull(u, l, r, p);
        return ;
    }
    int mid = l + r >> 1;
    pushdown(u, l, r, p);
    if (ln <= mid) modify(u << 1, l, mid, ln, rn, p);
    if (rn > mid) modify(u << 1 | 1, mid + 1, r, ln, rn, p);
    pushup(u, p);
}

i64 query(int u, int l, int r, int ln, int rn, int p) {
    if (ln <= l && r <= rn) return val[u][p];
    int mid = l + r >> 1;
    pushdown(u, l, r, p);
    i64 ans = 0;
    if (ln <= mid) ans += query(u << 1, l, mid, ln, rn, p);
    if (rn > mid) ans += query(u << 1 | 1, mid + 1, r, ln, rn, p);
    return ans;
}

int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
    int n; std::cin >> n;
    rep(i,1,n + 1) std::cin >> a[i];
    rep(i,0,20) digit[i] = 1 << i;
    rep(i,0,20) build(1, 1, n, i);

    int m; std::cin >> m;
    rep(i,0,m) {
        int op, l, r;
        std::cin >> op >> l >> r;
        if (op == 1) {
            i64 ans = 0;
            rep(j, 0, 20) ans += query(1, 1, n, l, r, j) * 1ll * digit[j];
            std::cout << ans << "\n";
        } else {
            int x;
            std::cin >> x;
            rep(j, 0, 20) if (x & digit[j]) modify(1, 1, n, l, r, j);
        }
    }
    return 0;
}
posted @ 2022-05-01 17:44  浅渊  阅读(24)  评论(0编辑  收藏  举报