就是对于一颗动态开点的线段树(而且一般为权值线段树),我们把它拆开拆成两份(一般来说:为了方便,按sz分裂)

可以用来干嘛呢,小编也很疑惑,或许是可以用于把这边线段树的区间转移到另一边?

那么废话不多说直接开始。

实现原理

有点类似于求排名为 $k$ 的数的过程,考虑其左子树的值,如果这个值大于当前分裂线(我们设为 $k$),我们直接就是把右子树挂到新树上,进入左子树。否则就是直接进右子树。当然如果直接相等了,我们只需要挂上右子树即可。(我们可以使用 $Swap$ 来实现挂树的操作)

原理不难理解,那就直接

Code:

	void split(int x,int &y,LL k){
		if(!x) return;
		y=new_node();
		LL key=t[lc(x)].cnt;
		if(key<k){
			split(rc(x),rc(y),k-key);
		}else Swap(rc(x),rc(y));
		if(key>k){
			split(lc(x),lc(y),k);
		}
		t[y].cnt=t[x].cnt-k;
		t[x].cnt=k;
	}

当然线段树分裂一般配合线段树合并一起使用,那么我给出模板题的代码。

Code:

#include<bits/stdc++.h>
using namespace std;
#define LL long long 
#define file(a) freopen(#a".in","r",stdin),freopen(#a".out","w",stdout)
inline int read(){
	int s=0,f=1;
	char ch=getchar();
	while(ch<'0'||'9'<ch) {if(ch=='-') f=-1;ch=getchar();}
	while('0'<=ch&&ch<='9') {s=s*10+(ch^48);ch=getchar();}
	return s*f;
}
inline void Swap(int &x,int &y){
	int tmp=x;x=y;y=tmp;
}
const int N=200000+3;
int n,Q;
struct SegTree{
	#define lc(x) t[x].ls
	#define rc(x) t[x].rs
	int bin[N<<5];
	int tot,top;
	struct Node{
		int ls,rs;
		LL cnt;
	}t[N<<5];
	inline int new_node(){
		return !top?(++tot):(bin[top--]);
	}
	inline void del_node(int x){
		bin[++top]=x;
		t[x].ls=t[x].rs=t[x].cnt=0;
	}
	inline void pushup(int i){
		t[i].cnt=(t[lc(i)].cnt+t[rc(i)].cnt);
	}
	void ins(int &i,int l,int r,int p,int k){
		if(!i){
			i=new_node();
			//printf("%d %d %d %d %d\n",i,l,r,p,k);
		}
		if(p<=l&&r<=p){
			t[i].cnt+=k;
			//printf("%d %d %d\n",i,l,t[i].cnt);
			return;
		}
		int mid=(l+r)>>1;
		if(p<=mid) ins(lc(i),l,mid,p,k);
		if(mid+1<=p) ins(rc(i),mid+1,r,p,k);
		pushup(i);
	}
	LL query(int i,int l,int r,int ql,int qr){
		//if(!i) return 0;
		//if(ql==1&&qr==1) printf("	%d %d %d %d %d\n",i,l,r,ql,qr,t[i].cnt);
		if(ql<=l&&r<=qr){
			return t[i].cnt;
		}
		LL res=0;
		int mid=(l+r)>>1;
		if(ql<=mid) res+=query(lc(i),l,mid,ql,qr);
		if(mid+1<=qr) res+=query(rc(i),mid+1,r,ql,qr);
		return res;
	}
	int merge(int x,int y,int l,int r){
		if(!x||!y) return x|y;
		if(l==r){
			t[x].cnt+=t[y].cnt;
			return x;
		}
		int mid=(l+r)>>1;
		lc(x)=merge(lc(x),lc(y),l,mid);
		rc(x)=merge(rc(x),rc(y),mid+1,r);
		pushup(x);
		del_node(y);
		return x;
	}
	void split(int x,int &y,LL k){
		if(!x) return;
		y=new_node();
		LL key=t[lc(x)].cnt;
		if(key<k){
			split(rc(x),rc(y),k-key);
		}else Swap(rc(x),rc(y));
		if(key>k){
			split(lc(x),lc(y),k);
		}
		t[y].cnt=t[x].cnt-k;
		//t[y].val=t[x].val-k;
		t[x].cnt=k;
		//t[x].val=k;
	}
	int kth(int i,LL k,int l,int r){
		if(l==r){
			return l;
		} 
		int mid=(l+r)>>1;
		LL key=t[lc(i)].cnt;
		if(k<=key) return kth(lc(i),k,l,mid);
		else return kth(rc(i),k-key,mid+1,r);
	}
	#undef lc
	#undef rc
}s;
int tot=1,home[N];
int main(){
	//file(a);
	n=read();Q=read();
	for(int i=1;i<=n;++i){
		int x=read();
		s.ins(home[1],1,n,i,x);
		//printf("%d\n",s.query(home[1],1,n,1,1));
		//printf("%d\n",home[1]);
	}
	while(Q--){
		int opt=read();
		if(opt==0){
			int x=read(),y=read(),z=read();
			int root=0;
			LL k1=s.query(home[x],1,n,1,z),k2=s.query(home[x],1,n,y,z);
			s.split(home[x],home[++tot],k1-k2);
			s.split(home[tot],root,k2);
			home[x]=s.merge(home[x],root,1,n);
		}else if(opt==1){
			int x=read(),y=read();
			home[x]=s.merge(home[x],home[y],1,n);
		}else if(opt==2){
			int a=read(),t=read(),x=read();
			s.ins(home[a],1,n,x,t);
		}else if(opt==3){
			int a=read(),l=read(),r=read();
			printf("%lld\n",s.query(home[a],1,n,l,r));
		}else if(opt==4){
			int a=read();
			LL k;scanf("%lld",&k);
			if(s.t[home[a]].cnt<k) printf("-1\n");
			else printf("%d\n",s.kth(home[a],k,1,n));
		}
		/*
		for(int i=1;i<=tot;++i){
			putchar('	');printf("%d(%d)->",i,home[i]);
			for(int j=1;j<=n;++j){
				printf("%d ",s.query(home[i],1,n,j,j));
			}
			putchar('\n');
		}
		*/
	}
	return 0;
}