1. 程式人生 > 實用技巧 >多校第十二場 題解

多校第十二場 題解

A. string

對於這類字串匹配題,有個套路是直接用 \(FFT\) 優化。
然後發現這題字符集很小,所以列舉一個字元,將匹配串中的這個字元設為 \(1\),模式串中的非這個字元設為 \(1\)
然後用一些技巧優化一下,就可以用 \(|\sum|+1\) 次長度為 \(n\)\(DFT\) 求出答案了。
 

B. Tree

容易發現這題就是通過與 \(LCT\) 類似的樹構造出深度和最大的原樹。
若一個節點在實鏈上,我們只關注每個深度的當前最大值。
若一個節點上面是虛邊,我們只需要這個節點作為 \(splay\) 根節點的當前最大值。
所以可以設計一個 \(dp\)\(dp_{i,j}\)

表示以 \(i\) 為根節點,\(splay\) 大小為 \(j\) 的最大值。
\(f_i=\max \limits_{j=1}^{sz_i}dp_{i,j}\) 這個玩意表示若 \(i\) 為虛子樹的最大值。
發現轉移主要與左子樹有關,因為左子樹貢獻了一些深度。
所以列舉左子樹是誰,列舉左子樹的大小就行了。
 

C. sort

如果只有排序操作,一個方法是這樣的。
開一個平衡樹維護已經排好序的每個連續段。
對於每個連續段,用 平衡樹/\(01trie\) 等來實現分裂,暴力去合併複雜度就是對的。
拓展到本題上,仍然使用平衡樹套 \(01trie\)
在平衡樹上打標記,表示對子樹內所有 \(01trie\)

都進行這種操作。
\(01trie\) 上也打標記,表示對子樹內節點進行這種操作。
可以認為,平衡樹上的標記是上次排序到這次排序之間的,也就是說不影響相對關係的。
\(01trie\) 上的標記是上次排序之前的,也就是說影響相對關係。
所以當 \(01trie\) 上標記下傳的時候,需要交換兩個兒子或者合併兩個兒子。
對於修改操作,只需要在區間的平衡樹上打個標記。
對於排序操作,可以暴力拉出來每個連續段,把它上面的標記打在 \(01trie\) 上,併合並在一起。
思路大概就是這樣,打起來有點噁心。
 



#include<bits/stdc++.h>
using namespace std;
const int N=1e5+7;
int n,m;
int a[N];
struct Tag{
	int A,B,C;
	Tag():A(-1),B(0),C(0){}
	inline friend void operator *= (Tag &a,const Tag &b){
		a.A&=b.A; a.B&=b.A; a.C&=b.A;
		a.B|=b.B; a.C&=-1^b.B; a.C^=b.C;
	}
	inline int mp(int k,int f){
		return ((f&(A>>k&1))|(B>>k&1))^(C>>k&1);
	}
};
namespace Trie{
	int cnt;
	int ch[N*180][2],sz[N*180];
	Tag lzy[N*180];
	inline void down(int p);
	inline int merge(int x,int y,int dep);
	inline void insert(int p,int x){
		for(int i=31;~i;--i){
			if(!ch[p][x>>i&1]) ch[p][x>>i&1]=++cnt;
			++sz[p]; p=ch[p][x>>i&1];
		} ++sz[p];
	}
	inline void update(int p){
		sz[p]=sz[ch[p][0]]+sz[ch[p][1]];
	}
	inline void down(int p,int dep){
		lzy[ch[p][0]]*=lzy[p]; lzy[ch[p][1]]*=lzy[p];
		if(lzy[p].mp(dep-1,0)==1&&lzy[p].mp(dep-1,1)==0) swap(ch[p][0],ch[p][1]);
		else if(lzy[p].mp(dep-1,0)==0&&lzy[p].mp(dep-1,1)==0) ch[p][0]=merge(ch[p][0],ch[p][1],dep-1),ch[p][1]=0;
		else if(lzy[p].mp(dep-1,0)==1&&lzy[p].mp(dep-1,1)==1) ch[p][1]=merge(ch[p][0],ch[p][1],dep-1),ch[p][0]=0;
		lzy[p]=Tag();
	}
	inline int merge(int x,int y,int dep){
		if(!x||!y) return x|y;
		sz[x]+=sz[y]; if(!dep) return x;
		down(x,dep); down(y,dep);
		ch[x][0]=merge(ch[x][0],ch[y][0],dep-1);
		ch[x][1]=merge(ch[x][1],ch[y][1],dep-1);
		return update(x),x;
	}
	inline void split(int p,int k,int dep,int &x,int &y){
		if(!dep) return x=++cnt,y=++cnt,sz[x]=k,sz[y]=sz[p]-k,void();
		down(p,dep); x=++cnt; y=++cnt;
		if(sz[ch[p][0]]>=k) split(ch[p][0],k,dep-1,ch[x][0],ch[y][0]),ch[y][1]=ch[p][1],update(x),update(y);
		else split(ch[p][1],k-sz[ch[p][0]],dep-1,ch[x][1],ch[y][1]),ch[x][0]=ch[p][0],update(x),update(y);
	}
	void dfs(int x,int dep,int ret,Tag now){
		if(!dep){
			int ans=0;
			for(int i=0;i<32;++i) ans|=now.mp(i,ret>>i&1)<<i;
			for(int i=0;i<sz[x];++i) printf("%u ",ans);
			return ;
		}
		down(x,dep);
		if(ch[x][0]) dfs(ch[x][0],dep-1,ret,now);
		if(ch[x][1]) dfs(ch[x][1],dep-1,ret|(1<<dep-1),now);
	}
}
namespace Treap{
	int rt,cnt;
	int ch[N*15][2],sz[N*15],rnd[N*15],l[N*15],r[N*15],tr[N*15];
	Tag val[N*15],lzy[N*15];
	inline void update(int p){
		sz[p]=1+sz[ch[p][0]]+sz[ch[p][1]];
	}
	inline void down(int p){
		val[ch[p][0]]*=lzy[p]; val[ch[p][1]]*=lzy[p];
		lzy[ch[p][0]]*=lzy[p]; lzy[ch[p][1]]*=lzy[p];
		lzy[p]=Tag();
	}
	inline void split(int p,int k,int &x,int &y){
		if(!p) return x=y=0,void();
		down(p);
		if(sz[ch[p][0]]>=k) split(ch[p][0],k,x,ch[p][0]),y=p,update(p);
		else split(ch[p][1],k-sz[ch[p][0]]-1,ch[p][1],y),x=p,update(p);
	}
	inline int merge(int x,int y){
		if(!x||!y) return x|y;
		if(rnd[x]>rnd[y]) return down(x),ch[x][1]=merge(ch[x][1],y),update(x),x;
		else return down(y),ch[y][0]=merge(x,ch[y][0]),update(y),y;
	}
	inline int Rank(int p,int k,int ret=0){
		while(p){
			down(p);
			if(r[p]<=k) ret+=sz[ch[p][0]]+1,p=ch[p][1];
			else p=ch[p][0];
		}
		return ret;
	}
	inline void cut(int k){
		int a,b;
		split(rt,Rank(rt,k),rt,a);
		if(!a) return ;
		split(a,1,a,b);
		if(l[a]==k+1) return rt=merge(rt,merge(a,b)),void();
		int c=++cnt,d=++cnt,num=k-l[a]+1; val[c]=val[d]=val[a]; sz[c]=sz[d]=1;
		rnd[c]=rand(); rnd[d]=rand();
		Trie::split(tr[a],num,32,tr[c],tr[d]);
		l[c]=l[a]; r[c]=k; l[d]=k+1; r[d]=r[a];
		rt=merge(rt,merge(merge(c,d),b));
	}
	inline void Split(int L,int R,int &a,int &b,int &c){
		cut(L-1); cut(R);
		split(rt,Rank(rt,L-1),a,b);
		split(b,Rank(b,R),b,c);
	}
	void dfs(int x,int h){
		down(x);
		Trie::lzy[tr[x]]*=val[x];
		tr[h]=Trie::merge(tr[h],tr[x],32);
		if(ch[x][0]) dfs(ch[x][0],h);
		if(ch[x][1]) dfs(ch[x][1],h);
	}
}
inline int read(register int x=0,register char ch=getchar()){
	for(;!isdigit(ch);ch=getchar()) ;
	for(; isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
	return x;
}
int main(){
	freopen("sort.in","r",stdin);
	freopen("sort.out","w",stdout);
	n=read(); m=read();
	for(int i=1;i<=n;++i){
		a[i]=read(); ++Treap::cnt;
		Treap::l[i]=i; Treap::r[i]=i; Treap::sz[i]=1; Treap::rnd[i]=rand();
		Treap::tr[i]=++Trie::cnt; Trie::insert(Treap::tr[i],a[i]);
		Treap::rt=Treap::merge(Treap::rt,i);
	}
	for(int i=1,opt,l,r,x;i<=m;++i){
		opt=read(); l=read(); r=read();
		if(opt==1){
			x=read(); int a,b,c; Tag now=Tag();
			Treap::Split(l,r,a,b,c); now.A=-1; now.B=x; now.C=0;
			Treap::lzy[b]*=now; Treap::val[b]*=now;
			Treap::rt=Treap::merge(Treap::merge(a,b),c);
		}
		else if(opt==2){
			x=read(); int a,b,c; Tag now=Tag();
			Treap::Split(l,r,a,b,c); now.A=x; now.B=0; now.C=0;
			Treap::lzy[b]*=now; Treap::val[b]*=now;
			Treap::rt=Treap::merge(Treap::merge(a,b),c);
		}
		else if(opt==3){
			x=read(); int a,b,c; Tag now=Tag();
			Treap::Split(l,r,a,b,c); now.A=-1; now.B=0; now.C=x;
			Treap::lzy[b]*=now; Treap::val[b]*=now;
			Treap::rt=Treap::merge(Treap::merge(a,b),c);
		}
		else{
			int a,b,c,d;
			Treap::Split(l,r,a,b,c); d=++Treap::cnt; Treap::rnd[d]=rand();
			Treap::sz[d]=1; Treap::l[d]=l; Treap::r[d]=r; Treap::dfs(b,d);
			Treap::rt=Treap::merge(Treap::merge(a,d),c);
		}
	}
	while(Treap::rt){
		int x; Treap::split(Treap::rt,1,x,Treap::rt);
		Trie::dfs(Treap::tr[x],32,0,Treap::val[x]);
	}
	return puts(""),0;
}