早已离我远去的六一儿童节……
维护区间翻转,很容易可以想到用(Splay)来维护
唯一的难点就是如何维护操作(2)
因为异或是对于每一个二进制位上的操作,而(d leq 2 ^ {20}),所以对于节点(x),我们可以开一个数组a[i]
,表示(x)和它的子树中第(i)个二进制位上的(1)的总和
题目比较卡常,需要开(o2)优化
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ls tree[p].s[0]
#define rs tree[p].s[1]
#define LL long long
using namespace std;
LL read() {
LL k = 0; char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9')
k = k * 10 + c - 48, c= getchar();
return k;
}
int root, tot;
struct zzz {
int s[2], num, fa;
LL a[25], val, sum, tag2;
bool tag;
}tree[100010];
void up(int p) {
tree[p].sum = tree[ls].sum + tree[rs].sum + tree[p].val;
tree[p].num = tree[ls].num + tree[rs].num + 1;
for(int i = 0; i <= 20; ++i)
tree[p].a[i] = tree[ls].a[i] + tree[rs].a[i] + ((tree[p].val >> i) & 1);
}
void Xor(int p, LL x) {
LL k = 0;
for(int i = 0; i <= 20; ++i) {
if((x >> i) & 1) tree[p].a[i] = tree[p].num - tree[p].a[i];
k += 1ll * (1 << i) * tree[p].a[i];
}
//cout << k << endl;
tree[p].tag2 ^= x; tree[p].val ^= x; tree[p].sum = k;
}
void rev(int p) {
if(!p) return ;
tree[p].tag ^= 1;
swap(ls, rs);
}
void down(int p) {
if(tree[p].tag) {
rev(ls), rev(rs);
tree[p].tag = 0;
}
if(tree[p].tag2) {
Xor(ls, tree[p].tag2);
Xor(rs, tree[p].tag2);
tree[p].tag2 = 0;
}
}
void rotate(int p) {
int f = tree[p].fa;
int ff = tree[f].fa;
bool k = tree[f].s[1] == p;
tree[ff].s[tree[ff].s[1] == f] = p;
tree[p].fa = ff;
tree[f].s[k] = tree[p].s[k ^ 1];
tree[tree[p].s[k ^ 1]].fa = f;
tree[p].s[k ^ 1] = f;
tree[f].fa = p;
up(f), up(p);
}
void Splay(int p, int goal) {
while(tree[p].fa != goal) {
int f = tree[p].fa;
int ff = tree[f].fa;
if(ff != goal)
(tree[f].s[0] == p) ^ (tree[ff].s[0] == f) ? rotate(p) : rotate(f);
rotate(p);
}
if(goal == 0) root = p;
}
LL a[100010];
void build(int &p, int l, int r) {
if(l > r) return ;
int mid = (l + r) >> 1;
p = mid;
tree[p].val = a[p];
build(ls, l, mid-1), build(rs, mid+1, r);
if(ls) tree[ls].fa = p; if(rs) tree[rs].fa = p;
up(p);
}
int k_th(int x) {
int p = root;
if(tree[p].num < x) return false;
while(p) {
down(p);
if(x > tree[ls].num + 1)
x -= tree[ls].num + 1, p = rs;
else if(tree[ls].num >= x) p = ls;
else return p;
}
return p;
}
int main() {
int n = read(), m = read();
for(int i = 1; i <= n; ++i) a[i+1] = read();
build(root, 1, n+2);
while(m--) {
int opt = read(), l = read(), r = read();
l = k_th(l); r = k_th(r+2);
Splay(l, 0); Splay(r, root);
if(opt == 1) {
rev(tree[tree[root].s[1]].s[0]);
}
else if(opt == 2) {
Xor(tree[tree[root].s[1]].s[0], read());
}
else if(opt == 3) {
printf("%lld
", tree[tree[tree[root].s[1]].s[0]].sum);
}
}
return 0;
}
弱化版题目:CF242E XOR on Segment
只有异或操作,无区间翻转,可只用线段树维护。当然,你把上面的代码直接改个if
也能过