Solution
观察到 ,拆位后数组变成 20 条长度为 的 01 串。开二十棵线段树分别维护这些串的区间和。
设一个串在区间 的和为 S,该区间与 异或后,新的和 ,即将区间内的 、 互换。
Code
#include <bits/stdc++.h>
#define ls k<<1
#define rs k<<1|1
using namespace std;
typedef long long ll;
const ll N = 1e5 + 10, logN = 21;
ll n, a[N], L[N << 2], R[N << 2], Q, siz[N << 2];
struct Tree {
ll sum, laz;
} T[N << 2][logN + 5];
void push_up(ll k)
{
for (int i = 0; i <= logN; i++)
T[k][i].sum = T[ls][i].sum + T[rs][i].sum;
}
void push_down(ll k)
{
for (int i = 0; i <= logN; i++) {
if (!T[k][i].laz) continue;
T[ls][i].laz ^= T[k][i].laz;
T[rs][i].laz ^= T[k][i].laz;
T[ls][i].sum = siz[ls] - T[ls][i].sum;
T[rs][i].sum = siz[rs] - T[rs][i].sum;
T[k][i].laz = 0;
}
}
void build(ll k, ll l, ll r)
{
L[k] = l; R[k] = r; siz[k] = r - l + 1;
if (l == r) {
ll num = a[l];
for (int i = 0; i <= logN; i++) {
T[k][i].sum = (num & 1);
num >>= 1;
}
return;
}
ll mid = (l + r) >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
push_up(k);
}
void modify(ll k, ll x, ll y, ll z)
{
if (x <= L[k] && R[k] <= y) {
ll num = z;
for (int i = 0; i <= logN; i++) {
if (num & 1)
T[k][i].sum = siz[k] - T[k][i].sum;
T[k][i].laz ^= (num & 1);
num >>= 1;
}
return;
}
ll mid = (L[k] + R[k]) >> 1;
push_down(k);
if (x <= mid) modify(ls, x, y, z);
if (mid < y) modify(rs, x, y, z);
push_up(k);
}
ll query(ll k, ll x, ll y)
{
ll res = 0;
if (x <= L[k] && R[k] <= y) {
for (int i = 0; i <= logN; i++)
res += (1 << i) * T[k][i].sum;
return res;
}
ll mid = (L[k] + R[k]) >> 1;
push_down(k);
if (x <= mid) res += query(ls, x, y);
if (mid < y) res += query(rs, x, y);
return res;
}
int main()
{
scanf("%lld", &n);
for (int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
build(1, 1, n);
scanf("%lld", &Q);
while (Q--) {
ll opt, x, y, z;
scanf("%lld%lld%lld", &opt, &x, &y);
if (opt == 1) {
printf("%lld\n", query(1, x, y));
} else {
scanf("%lld", &z);
modify(1, x, y, z);
}
}
return 0;
}