线段树分裂
可以裂开的线段树
就是对于一颗动态开点的线段树(而且一般为权值线段树),我们把它拆开拆成两份(一般来说:为了方便,按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;
}