对于0操作需要把区间x到y分裂出来变成一颗新的树(这像极了fhq的无旋treap) 我也用了类似的思想来实现
线段树维护每个权值出现的个数 2,3,4操作应该都不是问题
1操作的话就是线段树合并 没有什么特别的地方 对于y结点的删除 可以直接删 也可以把y结点放进内存池 我是用的内存池(不知道是不是这个名字。。。) 就是每删掉一个结点 可以把这个结点放入内存池 新建结点的时候 如果内存池有结点的话就从内存池里面取出结点 否则就新建一个结点 显然内存池可以减少空间复杂度 重复利用一些结点
int newnode(){//新建结点
return cs?mem[cs--]:++cnt;
}
void del(int x){//删除结点
mem[++cs]=x;val[x]=L[x]=R[x]=0;
}int merge(int x,int y){//合并操作
if(!x||!y) return x+y;
val[x]+=val[y];
L[x]=merge(L[x],L[y]);
R[x]=merge(R[x],R[y]);
del(y);
return x;
}对于0操作 我们要先计算树p中 1~y x~y的和分别为多少 设分别为cnt1,cnt2 那么我们要分裂成 1~cnt1,cnt1~cnt2,cnt2~n这三棵树 再把第一棵和第三棵合并起来 分裂函数 如下 y一开始是空树 所以要传引用 并且新建结点
void split(int x,int &y,ll k){
y=newnode();//y是一颗空树 每次都要赋予新结点
ll v = val[L[x]];
if(k>v) split(R[x],R[y],k-v);
else swap(R[x],R[y]);//如果k<=v那么x的右边属于y 把y的空右子树给x
if(k<v) split(L[x],L[y],k);
val[y]=val[x]-k;
val[x]=k;
}
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
typedef long long ll;
int tot=1,cnt,rt[N],L[N*40],R[N*40],mem[N*40],cs;
ll val[N*40];
int n,m;
int newnode(){
return cs?mem[cs--]:++cnt;
}
void del(int x){
mem[++cs]=x;val[x]=L[x]=R[x]=0;
}
void update(int &rt,int l,int r,int pos,int v){
if(!rt) rt=newnode();
val[rt]+=v;
if(l==r) return;
int mid = l+r>>1;
if(pos<=mid) update(L[rt],l,mid,pos,v);
else update(R[rt],mid+1,r,pos,v);
}
ll query(int rt,int l,int r,int ql,int qr){
if(!rt) return 0;
if(ql<=l&&qr>=r) return val[rt];
int mid = l+r>>1;
ll ans = 0;
if(ql<=mid) ans+=query(L[rt],l,mid,ql,qr);
if(qr>mid) ans+=query(R[rt],mid+1,r,ql,qr);
return ans;
}
int kth(int rt,int l,int r,ll k){
if(l==r) return l;
int mid = l+r>>1;
if(k<=val[L[rt]]) return kth(L[rt],l,mid,k);
else return kth(R[rt],mid+1,r,k-val[L[rt]]);
}
int merge(int x,int y){
if(!x||!y) return x+y;
val[x]+=val[y];
L[x]=merge(L[x],L[y]);
R[x]=merge(R[x],R[y]);
del(y);
return x;
}
void split(int x,int &y,ll k){
y=newnode();//y是一颗空树 每次都要赋予新结点
ll v = val[L[x]];
if(k>v) split(R[x],R[y],k-v);
else swap(R[x],R[y]);//如果k<=v那么x的右边属于y 把y的空右子树给x
if(k<v) split(L[x],L[y],k);
val[y]=val[x]-k;
val[x]=k;
}
int main(){
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++){
int x;
scanf("%d",&x);
update(rt[1],1,n,i,x);
}
int op,p,a,b;
for(int i = 1; i <= m; i++){
scanf("%d%d%d",&op,&p,&a);
if(op==0){
scanf("%d",&b);
int now;
ll cnt1=query(rt[p],1,n,a,b),cnt2=query(rt[p],1,n,1,b);
split(rt[p],rt[++tot],cnt2-cnt1);
split(rt[tot],now,cnt1);
rt[p]=merge(rt[p],now);
}else if(op==1){
rt[p]=merge(rt[p],rt[a]);
}else if(op==2){
scanf("%d",&b);
update(rt[p],1,n,b,a);
}else if(op==3){
scanf("%d",&b);
printf("%lld\n",query(rt[p],1,n,a,b));
}else{
if(a>val[rt[p]]) puts("-1");
else printf("%d\n",kth(rt[p],1,n,a));
}
}
return 0;
}