Codeforces 1515 H. Phoenix and Bits 题解

Codeforces 1515 H. Phoenix and Bits

题意

给出一个序列,有四种操作。

  1. 对于所有\(l \leq a_i \leq r\) ,使\(a_i = a_i \ AND \ x\)
  2. 对于所有\(l \leq a_i \leq r\) ,使\(a_i = a_i \ OR \ x\)
  3. 对于所有\(l \leq a_i \leq r\) ,使\(a_i = a_i \ XOR \ x\)
  4. 对于所有\(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\)时的边界条件。

posted @ 2021-05-04 14:04  趁着胆子小  阅读(163)  评论(0编辑  收藏  举报
//explotion effect (unabled)