CF 242E. Xor on Segment
Xor on Segment
题意
给出 \(n\) 个数字的序列 \(a\) 。进行 \(m\) 次操作,操作有两种:
- 求 \(\displaystyle \sum_{i=l}^{r} a_i\) 。
- 把区间 \([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;
}