sort(区间异或,区间排序,区间或,区间与,Trie树合并,Treap)

看标题就知道是大工业题

四种操作(标题前四个短语)
求最后的序列。

n 1 e 5 , v 2 32 n \leq 1e5 , v \leq 2^{32}

考虑只有排序:
发现有序的一段可以直接用一个 01 t r i e 01trie 树来存储, t r i e trie 树上字典序从小到大的值就是这一段从左到右的值。
那么区间排序 [ l , r ] [l,r] 时就相当于把这个区间 [ l , r ] [l,r] 的所有数字放到一个 t r i e trie T 1 T_1 里面,用一个三元组 [ l , r , T 1 ] [l,r,T_1] 表示这个区间。
熟悉珂朵莉树的同学可以马上发现其实这就是一个序列上有很多个连续的线段,每次我们需要断开一些线段,合并一些线段来使得 [ l , r ] [l,r] 是同一个线段并且没有别的点在里面。
可以通过平衡树维护 [ l , r , T ] [l,r,T] ,每次就是推平一个区间,然后 T r i e Trie 树合并。
分裂一个有序的区间就是 T r i e Trie 树分裂。

复杂度 O ( n log n ) O(n\log n) 证明:(注意这个性质将贯穿本题之后的复杂度证明)
1. T r i e Trie (线段树)合并的复杂度是被删除的总节点个数(也就是两个线段树重合的节点个数。)
2.每次分裂至多增加 O ( log n ) O(\log n) 个节点。
所以复杂度为 O ( n log n ) O(n\log n)

考虑加入区间异或:
发现一个区间必须全局异或,所以我们每次将操作区间预先分裂好。
然后就是对于 [ l , r ] [l,r] 内的所有区间,我们都要给区间内的 T r i e Trie 树打上标记。
发现普通的珂朵莉树到这里因为没有区间推平所以复杂度是错的。
只需要将实现珂朵莉树的 s e t set 改为 T r e a p Treap 即可实现区间打标记。
注意到对于 T r i e Trie 如果我们在维护全局异或时改变他的形态,那么区间有序的性质就无法维护。
所以我们只在接受排序时把全局异或标记打在 T r i e Trie 的根上,下放的时候视标记交换左右儿子即可,其他时候就存着不用即可。
可以发现只有排序的时候需要打异或标记,别的时候都可以简单维护。

考虑加入区间或/与:
类似于区间异或,只是下放标记的时候不是交换左右儿子,而是合并左右儿子到左/右边。
因为复杂度只和总节点数有关,所以看上去如此离谱的操作也是总复杂度 O ( n log n ) O(n\log n) 的。

1e5要跑2s的n\log n

A C   C o d e \mathcal AC \ Code

#include<bits/stdc++.h>
#define maxn 200005
#define maxp maxn * 70
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long
#define ui unsigned int 
using namespace std;

char cb[1<<16],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<16,stdin),cs==ct)?0:*cs++)
template<class T>void read(T &res){
	char ch;
	for(;!isdigit(ch=getc()););
	for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}

char Start;

#define MX 4294967295u
int n,m;
int rt[maxn];

namespace Trie{
	int tr[maxp][2],sz[maxp],bin[maxp],tot;
	ui AND[maxp],XOR[maxp],OR[maxp];
	int newnode(){
		int r;
		/*if(bin[0]) r = bin[bin[0] --];
		else */r = ++tot;
		tr[r][0] = tr[r][1] = sz[r] = 
		OR[r] = XOR[r] = 0;
		AND[r] = MX;
		return r;
	}
	void ins(int &u,ui a){
		if(!u) u = newnode();
		int x = u;
		per(i,31,0){
			int d = a >> i & 1;
			sz[x] ++;
			if(!tr[x][d]) tr[x][d] = newnode();
			x = tr[x][d];
		}
		sz[x] ++;
	}	
	void merge(int &u,int l,int r,int d=31);
	void dtXOR(int u,ui a,int d){
		if(d < 0 || !a) return;
		if(a >> d & 1) swap(tr[u][0] , tr[u][1]); 
		XOR[u] ^= a;
	}
	void dtAND(int u,ui x,int d){
		if(d < 0 || x == MX) return;
		XOR[u] &= x , AND[u] &= x;
		//printf("@%u %u\n",x,AND[u]);
		if((x >> d & 1) == 0)
			merge(tr[u][0] , tr[u][0] , tr[u][1],d - 1) , tr[u][1] = 0; 
	}
	void dtOR(int u,ui x,int d){
		if(d < 0 || !x) return;
		OR[u] |= x , XOR[u] &= MX - x , AND[u] |= x;
		if((x >> d & 1))
			merge(tr[u][1] , tr[u][0] , tr[u][1],d - 1) , tr[u][0] = 0;
	}
	void dt(int u,int d){
		if(OR[u]) dtOR(tr[u][0],OR[u],d-1) , dtOR(tr[u][1],OR[u],d-1),OR[u] = 0;
		if(AND[u] < MX) dtAND(tr[u][0],AND[u],d-1) , dtAND(tr[u][1],AND[u],d-1),AND[u] = MX;
		if(XOR[u]) dtXOR(tr[u][0],XOR[u],d-1) , dtXOR(tr[u][1],XOR[u],d-1) , XOR[u] = 0;
	}
	void merge(int &u,int l,int r,int d){
		if(!l || !r) return (void)(u = l + r);
		if(d >= 0){
			dt(l,d) , dt(r,d);
			u = l;
			merge(tr[u][0],tr[l][0],tr[r][0],d-1) , merge(tr[u][1],tr[l][1],tr[r][1],d-1);
			sz[u] = sz[tr[u][0]] + sz[tr[u][1]];
		}
		else{
			u = l;
			sz[u] = sz[l] + sz[r];
		}
		bin[++bin[0]] = r;
	}
	void split(int u,int &l,int &r,int v,int d=31){
		if(!u) return (void)( l = r = 0);
		if(d == -1){
			int u2 = newnode();
			sz[u2] = sz[u] - v , sz[u] = v;
			l = u , r = u2;
			return;
		}
		dt(u,d);
		int u2 = newnode();
		int L = tr[u][0] , R = tr[u][1];
		sz[u2] = sz[u] - v , sz[u] = v , tr[u2][1] = tr[u][1];
		tr[u][1] = 0 , tr[u2][0] = 0;
		l = u , r = u2;
		if(sz[tr[u][0]] > v) split(L , tr[l][0] , tr[r][0] , v , d - 1);
		else split(R , tr[l][1] , tr[r][1] , v - sz[tr[u][0]] , d - 1);
	}
	ui Find(int u,int d = 31){
		if(d == -1) return 0;
		dt(u,d);
		if(sz[tr[u][0]]) return Find(tr[u][0],d-1);
		return Find(tr[u][1],d-1) + (1u << d);
	}
}

int Ran(){ 
	static int G = 3;
	return (G = (3ll * G) %  998244353);
}

namespace Treap{
	int lc[maxn],rc[maxn],bin[maxn],sz[maxn],ls[maxn],rs[maxn],tot;
	ui XOR[maxn][2],OR[maxn][2],AND[maxn][2];
	int newnode(int L,int R){ 
		int r;
		if(bin[0]) r = bin[bin[0] --];
		else r = ++tot;
		lc[r] = rc[r] = rt[r] = 
		XOR[r][0] = OR[r][0] =
		XOR[r][1] = OR[r][1] = 0 ;
		AND[r][0] = AND[r][1] = MX;
		sz[r] = 1;
		ls[r] = L , rs[r] = R;
		return r;
	}
	void upd(int u){ sz[u] = sz[lc[u]] + sz[rc[u]] + 1;	}
	void dtXOR(int u,ui x){ if(x) rep(t,0,1) XOR[u][t] ^= x; }
	void dtAND(int u,ui x){ if(x != MX)rep(t,0,1) AND[u][t] &= x , XOR[u][t] &= x; }
	void dtOR(int u,ui x){ if(x)rep(t,0,1)OR[u][t] |= x , XOR[u][t] &= MX - x , AND[u][t] |= x; }
	void dt(int u){
		if(!u) return;
		if(OR[u][0]) dtOR(lc[u],OR[u][0]) , dtOR(rc[u],OR[u][0]) , OR[u][0] = 0;
		if(AND[u][0] < MX) dtAND(lc[u],AND[u][0]) , dtAND(rc[u],AND[u][0]) , AND[u][0] = MX;
		if(XOR[u][0]) dtXOR(lc[u],XOR[u][0]) , dtXOR(rc[u],XOR[u][0]) , XOR[u][0] = 0;
	}
	void split(int u,int &l,int &r,int v){
		if(!u) return (void)(l = r = 0);
		dt(u);
		if(rs[u] <= v) l = u , split(rc[u],rc[l],r,v),upd(u);
		else if(ls[u] > v) r = u , split(lc[u],l,lc[r],v),upd(u);
		else{
			int u2 = newnode(v+1,rs[u]);
			Trie::split(rt[u],rt[u],rt[u2],v-ls[u]+1);
			XOR[u2][1] = XOR[u][1] , OR[u2][1] = OR[u][1] , AND[u2][1] = AND[u][1];
			rs[u] = v;
			rc[u2] = rc[u] , rc[u] = 0;
			l = u , r = u2;
			upd(u) , upd(u2);
		}
	}
	void merge(int &u,int l,int r){
		if(!l || !r) return (void)(u = l+r);
		dt(l),dt(r);
		if(Ran() % (sz[l] + sz[r]) < sz[l]) u = l , merge(rc[u],rc[l],r);
		else u = r , merge(lc[u],l,lc[r]);
		upd(u);
	}
	void allmerge(int &u,int R){
		if(!u) return;
		Trie::dtOR(rt[u],OR[u][1],31);
		Trie::dtAND(rt[u],AND[u][1],31);
		Trie::dtXOR(rt[u],XOR[u][1],31);
		dt(u);
		allmerge(lc[u],R);
	//	printf("@%d %d %d\n",Trie::sz[rt[u]],Trie::tr[rt[u]][0] , Trie::tr[rt[u]][1]);
		Trie::merge(rt[R],rt[R],rt[u],31);
		allmerge(rc[u],R);
		bin[++bin[0]] = u;
		u = 0;
	}
}

int pt[maxn];

char End;

int main(){
	
	freopen("sort.in","r",stdin);
	freopen("sort.out","w",stdout);
	
	Trie::AND[0] = MX;
	
	cerr << (&End - &Start) / 1024 / 1024 << endl;
	read(n),read(m);
	rep(i,1,n){
		int t = Treap::newnode(i,i);
		ui x;read(x);
		Trie::ins(rt[t],x);
		Treap::merge(rt[0],rt[0],t);
	}
	ui v;
	for(int op,l,r,a,b,c,d;m--;){
		read(op) , read(l) , read(r);
		Treap::split(rt[0],b,c,r) , Treap::split(b,a,b,l-1);
		if(op == 4){
			int R = Treap::newnode(l,r);
		//	puts("####");
			Treap::allmerge(b,R);
			Treap::merge(rt[0],a,R);
			Treap::merge(rt[0],rt[0],c);;
		}
		else{
			read(v);
			if(op == 1) Treap::dtOR(b,v);
			if(op == 2) Treap::dtAND(b,v);
			if(op == 3) Treap::dtXOR(b,v);
			Treap::merge(rt[0],a,b);
			Treap::merge(rt[0],rt[0],c);
		}
	}
	per(i,n,1){
		Treap::split(rt[0],rt[0],pt[i],i-1);
		
		int u = pt[i];
		Trie::dtOR(rt[u],Treap::OR[u][1],31);
		Trie::dtAND(rt[u],Treap::AND[u][1],31);
		Trie::dtXOR(rt[u],Treap::XOR[u][1],31);
	}
	rep(i,1,n) printf("%u%c",Trie::Find(rt[pt[i]],31)," \n"[i==n]);
}

猜你喜欢

转载自blog.csdn.net/qq_35950004/article/details/107582119