P5494 线段树分裂
https://www.luogu.com.cn/problem/P5494
线段树合并在这里:https://www.cnblogs.com/suxxsfe/p/13839555.html
这个分裂其实和 fhq-treap 有点像,就是把一棵树分裂为两棵树,把第一棵树中的 \([l,r]\) 节点分裂到第二个树
过程大概是这样的,设被分裂的树为 \(a\),分裂到 \(b\)
- 如果 \(a\) 为空,那么肯定直接返回
- 如果要分裂出的 \([l,r]\) 区间完全覆盖了当前区间,整棵树都应该给 \(b\),也就是
b=a,a=null
- 根据区间的情况,考虑是否要递归分裂 \(a,b\) 的左右子树
- 注意两棵树的指针都要传引用
复杂度显然 \(O(\log n)\)
所以也没什么很难的地方
那么看这个题,先建立权值线段树,\(0\) 操作分裂出 \([x,y]\) 到一个新树,\(1\) 操作将整个 \(t\) 树 merge
到 \(p\),\(2,3,4\) 几个操作都是线段树的基本操作了
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#define reg register
#define EN puts("")
inline int read(){
register int x=0;register int y=1;
register char c=std::getchar();
while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
return y?x:-x;
}
#define N 400006
int n,m,Now;
struct Node{
Node *ls,*rs;
long long cnt;
}dizhi[N*30],*root[N],*null=&dizhi[0];
int tot;
inline void New(Node *&a){
a=&dizhi[++tot];a->ls=a->rs=null;
}
void change(Node *tree,int l,int r,int pos,int w){
tree->cnt+=w;
if(l==r) return;
int mid=(l+r)>>1;
if(pos<=mid){
if(tree->ls==null) New(tree->ls);
change(tree->ls,l,mid,pos,w);
}
else{
if(tree->rs==null) New(tree->rs);
change(tree->rs,mid+1,r,pos,w);
}
}
long long ask(Node *tree,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr) return tree->cnt;
int mid=(l+r)>>1;
long long ret=0;
if(ql<=mid&&tree->ls!=null) ret+=ask(tree->ls,l,mid,ql,qr);
if(qr>mid&&tree->rs!=null) ret+=ask(tree->rs,mid+1,r,ql,qr);
return ret;
}
int kth(Node *tree,int l,int r,int k){
if(l==r) return l;
int mid=(l+r)>>1;
if(k<=tree->ls->cnt) return kth(tree->ls,l,mid,k);
else return kth(tree->rs,mid+1,r,k-tree->ls->cnt);
}
inline void pushup(Node *tree){tree->cnt=tree->ls->cnt+tree->rs->cnt;}
Node *merge(Node *x,Node *y,int l,int r){
if(x==null) return y;
if(y==null) return x;
if(l==r) return x->cnt+=y->cnt,x;
int mid=(l+r)>>1;
x->ls=merge(x->ls,y->ls,l,mid);
x->rs=merge(x->rs,y->rs,mid+1,r);
pushup(x);
return x;
}
void split(Node *&a,Node *&b,int l,int r,int ql,int qr){
if(a==null) return;
if(ql<=l&&r<=qr) return b=a,a=null,void();//完全覆盖
if(b==null) New(b);
int mid=(l+r)>>1;
if(ql<=mid) split(a->ls,b->ls,l,mid,ql,qr);
if(qr>mid) split(a->rs,b->rs,mid+1,r,ql,qr);
pushup(a);pushup(b);
}
int main(){
n=read();m=read();
New(root[1]);Now=1;
for(reg int i=1;i<=n;i++) change(root[1],1,n,i,read());
reg int k,x,y,op;
while(m--){
op=read();
if(!op){
k=read();x=read();y=read();
New(root[++Now]);
split(root[k],root[Now],1,n,x,y);
}
else if(op==1){
x=read();y=read();
merge(root[x],root[y],1,n);
}
else if(op==2){
k=read();x=read();y=read();
change(root[k],1,n,y,x);
}
else if(op==3){
k=read();x=read();y=read();
printf("%lld\n",ask(root[k],1,n,x,y));
}
else{
x=read();k=read();
if(root[x]->cnt<k) puts("-1");
else printf("%d\n",kth(root[x],1,n,k));
}
}
return 0;
}