题解:P4883 mzf的考验
这是一道数据结构题。
前置知识
- fhq-treap
思路
看到区间翻转我们可以立刻想到平衡树,但是如何维护区间异或和区间求和呢?
我们考虑对于每个二进制位都开一棵平衡树,也可以在一棵平衡树上维护每个位的
我们考虑如何快速维护区间翻转、区间异或、区间查询,我们需要维护什么东西?首先,大小、左儿子、右儿子肯定是需要的,其次,每位的
我们考虑如何区间翻转,我们将这个区间分裂出来,然后将翻转标记异或上
然后是区间异或,我们将异或的数拆成一个长度为
关于区间翻转和区间异或懒标记下传的顺序其实不重要,不会互相影响。
上传的时候就直接将和相加,并加上当前节点的值,节点大小同理更新。
当前节点值也可以直接用 int 存储,然后直接异或,上传时再取出这一位,我的代码用的是数组存储。
区间查询时就直接分裂出这一个区间,然后将每一位的值乘上
时间复杂度是 bitset 代替 bool 数组(前文的懒标记),可以加快速度,但是不改变总体时间复杂度。
Code
#include<bits/stdc++.h>
#define ll long long
#define N 100005
using namespace std;
int n,Q,dat[N],res[20];
namespace Treap{
struct node{
int l,r,rnd,val[20],sum[20],siz;
bool rev,tag[20];
} tri[N];
int tot,root;
int new_node(){
int p = ++ tot;
for(int i = 0;i < 20;i ++){
tri[p].val[i] = tri[p].sum[i] = res[i];
}
tri[p].rnd = rand();
tri[p].siz = 1;
return p;
}
void pushup(int p){
tri[p].siz = tri[tri[p].l].siz + tri[tri[p].r].siz + 1;
for(int i = 0;i < 20;i ++){
tri[p].sum[i] = tri[tri[p].l].sum[i] + tri[tri[p].r].sum[i] + tri[p].val[i];
}
}
void pushdown(int p){
if(tri[p].rev == true){
swap(tri[p].l,tri[p].r);
if(tri[p].l != 0){
tri[tri[p].l].rev ^= true;
}
if(tri[p].r != 0){
tri[tri[p].r].rev ^= true;
}
tri[p].rev = false;
}
for(int i = 0;i < 20;i ++){
if(tri[p].tag[i] == true){
if(tri[p].l != 0){
tri[tri[p].l].tag[i] ^= true;
tri[tri[p].l].val[i] ^= 1;
tri[tri[p].l].sum[i] = tri[tri[p].l].siz - tri[tri[p].l].sum[i];
}
if(tri[p].r != 0){
tri[tri[p].r].tag[i] ^= true;
tri[tri[p].r].val[i] ^= 1;
tri[tri[p].r].sum[i] = tri[tri[p].r].siz - tri[tri[p].r].sum[i];
}
tri[p].tag[i] = false;
}
}
}
void split(int p,int k,int &x,int &y){
if(p == 0){
x = y = 0;
return;
}
pushdown(p);
if(tri[tri[p].l].siz >= k){
y = p;
split(tri[p].l,k,x,tri[p].l);
}else{
x = p;
split(tri[p].r,k - tri[tri[p].l].siz - 1,tri[p].r,y);
}
pushup(p);
}
int merge(int x,int y){
if(x == 0 or y == 0){
return x + y;
}
if(tri[x].rnd < tri[y].rnd){
pushdown(x);
tri[x].r = merge(tri[x].r,y);
pushup(x);
return x;
}else{
pushdown(y);
tri[y].l = merge(x,tri[y].l);
pushup(y);
return y;
}
}
void add(){
root = merge(root,new_node());
}
void reverse(int l,int r){
int x,y,z;
split(root,l - 1,x,y);
split(y,r - l + 1,y,z);
tri[y].rev ^= true;
root = merge(merge(x,y),z);
}
void update(int l,int r){
int x,y,z;
split(root,l - 1,x,y);
split(y,r - l + 1,y,z);
for(int i = 0;i < 20;i ++){
if(res[i] == true){
tri[y].tag[i] ^= true;
tri[y].val[i] ^= 1;
tri[y].sum[i] = tri[y].siz - tri[y].sum[i];
}
}
root = merge(merge(x,y),z);
}
void query(int l,int r){
int x,y,z;
split(root,l - 1,x,y);
split(y,r - l + 1,y,z);
for(int i = 0;i < 20;i ++){
res[i] = tri[y].sum[i];
}
root = merge(merge(x,y),z);
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> Q;
for(int i = 1;i <= n;i ++){
cin >> dat[i];
for(int j = 0;j < 20;j ++){
res[j] = (dat[i] >> j) & 1;
}
Treap::add();
}
while(Q --){
int opt,l,r;
cin >> opt >> l >> r;
if(opt == 1){
Treap::reverse(l,r);
}else if(opt == 2){
int k;
cin >> k;
for(int i = 0;i < 20;i ++){
res[i] = (k >> i) & 1;
}
Treap::update(l,r);
}else{
Treap::query(l,r);
ll ans = 0;
for(int i = 0;i < 20;i ++){
ans += (1ll << i) * (ll)(res[i]);
}
cout << ans << "\n";
}
}
return 0;
}