洛谷 线段树分裂模板

对于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;
}

猜你喜欢

转载自blog.csdn.net/weixin_43824564/article/details/106353020
今日推荐