Codeforces 1515 H. Phoenix and Bits 题解
Codeforces 1515 H. Phoenix and Bits
题意
给出一个序列,有四种操作。
- 对于所有\(l \leq a_i \leq r\) ,使\(a_i = a_i \ AND \ x\)
- 对于所有\(l \leq a_i \leq r\) ,使\(a_i = a_i \ OR \ x\)
- 对于所有\(l \leq a_i \leq r\) ,使\(a_i = a_i \ XOR \ x\)
- 对于所有\(l \leq a_i \leq r\) ,不同的\(a_i\)的个数。
考虑前三种操作,我们先将需要操作的子树\(split\)出来,操作完再\(merge\)回去。
\(a_i \ AND \ x\)可以规约为\(a_i \ XOR \ 2^{20}-1 , a_i \ OR \ x,a_i \ XOR \ 2^{20}-1\)
于是只需要考虑\(2,3\)操作,\(3\)操作可以通过打\(tag\)并交换子树实现,对于\(2\)操作,我们记录当前节点,有\(1\)的位置和有\(0\)的位置,\(x\)当前这位如果是\(1\),相当于把\(0\)节点全部合并到\(1\)节点上。如果既有\(0\)又有\(1\)就暴力递归\(merge\),否则就相当于交换子树,用\(2\)操作处理。
由于每次暴力\(merge\),节点数就会减少\(1\),递归到这个节点的复杂度等于它的深度,是\(O(logn)\)级别的, 节点数最多减少\(O(nlogn)\)次,所以总复杂度是\(O(nlog^2n)\)的。
Code
#include<bits/stdc++.h>
#define ll long long
#define N 200015
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=n;i>=a;i--)
#define inf 0x3f3f3f3f
#define pb push_back
#define mp make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define lowbit(i) ((i)&(-i))
#define VI vector<int>
#define all(x) x.begin(),x.end()
#define SZ(x) ((int)x.size())
using namespace std;
int n,q,a[N];
const int all = (1<<20)-1;
int ls[N<<5],rs[N<<5],lz[N<<5],t0[N<<5],t1[N<<5],tr[N<<5],rt,tot;
void pushup(int p){
tr[p] = tr[ls[p]]+tr[rs[p]];
t0[p] = t0[ls[p]]|t0[rs[p]];
t1[p] = t1[ls[p]]|t1[rs[p]];
}
void Xor(int p,int dep,int x){
if(!p) return;
if(x>>(dep-1)&1) swap(ls[p],rs[p]);
int v0 = (t1[p]&x)|(t0[p]&(~x)),v1 = (t0[p]&x)|(t1[p]&(~x));
t0[p] = v0; t1[p] = v1; lz[p] ^= x;
}
void pushdown(int p,int dep){
if(lz[p]){
Xor(ls[p],dep-1,lz[p]);
Xor(rs[p],dep-1,lz[p]);
lz[p] = 0;
}
}
void Split(int &u,int &v,int dep,int l,int r,int a,int b){
if(a <= l && r <= b){
v = u; u = 0;
return;
}
pushdown(u,dep);
v = ++tot;
int mid = (l+r)>>1;
if(a <= mid) Split(ls[u],ls[v],dep-1,l,mid,a,b);
if(b > mid) Split(rs[u],rs[v],dep-1,mid+1,r,a,b);
pushup(u); pushup(v);
}
void merge(int &u,int &v,int dep){
if(!u) return u = v,void();
if(!v || !dep) return;
pushdown(u,dep); pushdown(v,dep);
merge(ls[u],ls[v],dep-1); merge(rs[u],rs[v],dep-1);
pushup(u);
}
void ins(int &p,int x,int dep){
if(!p) p = ++tot;
if(!dep){
tr[p] = 1;
t0[p] = x^all;
t1[p] = x;
return;
}
if(x>>(dep-1)&1) ins(rs[p],x,dep-1);
else ins(ls[p],x,dep-1);
pushup(p);
}
void Or(int p,int dep,int x){
if(!p || !dep) return;
if(!(x&t0[p]&t1[p])){
Xor(p,dep,x&t0[p]);
return;
}
pushdown(p,dep);
if(x>>(dep-1)&1){
Xor(ls[p],dep-1,1<<(dep-1));
merge(rs[p],ls[p],dep-1);
ls[p] = 0;
}
Or(ls[p],dep-1,x);
Or(rs[p],dep-1,x);
pushup(p);
}
int query(int p,int dep,int l,int r,int x,int y){
if(x <= l && r <= y) return tr[p];
int mid = (l+r)>>1;
pushdown(p,dep);
int res = 0;
if(x <= mid) res += query(ls[p],dep-1,l,mid,x,y);
if(y > mid) res += query(rs[p],dep-1,mid+1,r,x,y);
return res;
}
int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
scanf("%d%d",&n,&q);
rep(i,1,n){
int x; scanf("%d",&x);
ins(rt,x,20);
}
while(q--){
int typ,l,r,x;
scanf("%d%d%d",&typ,&l,&r);
if(typ <= 3){
int v;
scanf("%d",&x);
Split(rt,v,20,0,all,l,r);
if(typ == 1) Xor(v,20,all),Or(v,20,x^all),Xor(v,20,all);
if(typ == 2) Or(v,20,x);
if(typ == 3) Xor(v,20,x);
merge(rt,v,20);
}else{
printf("%d\n",query(rt,20,0,all,l,r));
}
}
return 0;
}
!!!注意点!!!
递归时记得\(pushdown\),有修改的操作回溯时要\(pushup\),注意\(return\)时的边界条件。