CF 242E. Xor on Segment

Xor on Segment

题意

给出 \(n\) 个数字的序列 \(a\) 。进行 \(m\) 次操作,操作有两种:

  1. \(\displaystyle \sum_{i=l}^{r} a_i\)​​ 。
  2. 把区间 \([l, r]\) 上的每个数字异或 \(x\)

其中 \(1 \le n \le 10^5, 1 \le m \le 5 \times 10^4, 0 \le a_i \le 10^6, 1 \le x \le 10^6\)

分析

线段树 + 二进制拆位

异或修改与求区间和不能兼容,需要用到拆位线段树,把每个数字分解成20位二进制数,20棵线段树维护每个位上的 \(1\)​ 的个数。

这样异或修改就是对20棵树进行区间 \(01\) 反转,求区间和可以算出区间数字每个位上的 \(1\) 数量乘上该位的权重。

Code

#include <iostream>

using namespace std;

#define lc(x) (x<<1)
#define rc(x) (x<<1|1)
#define rep(i, x, y) for(int i = x; i <= y; i++)

using LL = long long ;

const int N = 100010;

struct seg_tree
{
    int l, r;
    int cnt; // 区间的 1 的个数
    bool tag;
};

seg_tree t[25][N<<2];
int n, m, a[N], w[N]; // p 记录二进制每个位置上的权值

void pushup (int tr, int p)
{
    t[tr][p].cnt = t[tr][lc(p)].cnt + t[tr][rc(p)].cnt;
}

void move_tag (int tr, int p)
{
    t[tr][p].cnt = t[tr][p].r - t[tr][p].l + 1 - t[tr][p].cnt;
    t[tr][p].tag ^= 1;
}

void pushdown (int tr, int p)
{
    if (t[tr][p].tag)
    {
        move_tag(tr, lc(p)); move_tag(tr, rc(p));
        t[tr][p].tag = false;
    }
}

void build (int tr, int p, int l, int r)
{
    t[tr][p].l = l, t[tr][p].r = r;
    t[tr][p].tag = false; t[tr][p].cnt = 0;
    if (l == r)
    {
        t[tr][p].cnt = (a[l] & w[tr]) != 0;
        return ;
    }
    int mid = l + r >> 1;
    build(tr, lc(p), l, mid); build(tr, rc(p), mid + 1, r);
    pushup(tr, p);
}

int query (int tr, int p, int l, int r)
{
    if (l <= t[tr][p].l && r >= t[tr][p].r) return t[tr][p].cnt;
    pushdown(tr, p);
    int ans = 0;
    int mid = t[tr][p].l + t[tr][p].r >> 1;
    if (l <= mid) ans += query(tr, lc(p), l, r);
    if (r > mid) ans += query(tr, rc(p), l, r);
    return ans ;
}

void update (int tr, int p, int l, int r)
{
    if (l <= t[tr][p].l && r >= t[tr][p].r)
    {
        move_tag(tr, p);
        return ;
    }
    pushdown(tr, p);
    int mid = t[tr][p].l + t[tr][p].r >> 1;
    if (l <= mid) update(tr, lc(p), l, r);
    if (r > mid) update(tr, rc(p), l, r);
    pushup(tr, p);
}

void solve ()
{
    rep(i, 0, 20) w[i] = (1 << i);
    cin >> n;
    rep(i, 1, n) cin >> a[i];
    rep(i, 0, 20) build(i, 1, 1, n); // 第i棵线段树,保存的是第i位的二进制数
    cin >> m;
    while (m -- )
    {
        int op, l, r; cin >> op >> l >> r;
        if (op == 1)
        {
            LL res = 0;
            // 看作一个大的数字,对每一二进制位求和
            rep(i, 0, 20) res += 1ll * w[i] * query(i, 1, l, r);
            cout << res << endl;
        }
        else
        {
            int x; cin >> x;
            // 注意,只有某一位上 x 为 1 时才会区间反转
            rep(i, 0, 20) if (x & w[i]) update(i, 1, l, r);
        }
    }
}

/* Storms make trees take deeper roots.*/
signed main () 
{
        solve();

    return 0;
}
posted @ 2021-11-20 13:47  Rainea  阅读(47)  评论(0)    收藏  举报