P5494-线段树合并与分裂/无旋Treap

本文详细介绍了线段树合并与分裂的操作方法,以及Treap的实现细节,包括节点的构建、合并、分裂和查询等核心算法。通过具体代码示例,深入解析了两种数据结构在处理区间操作和动态数据更新时的高效策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

P5494

题目描述

题目描述

题解

乍一眼看,平衡树啊!于是我直接敲了个FHQ上去,结果90分,至今没有调出来qwq
我们还是先讲讲FHQ的做法:
其实唯一需要讲的就是操作 1 1 1(其他操作都跟普通平衡树差不多,也比较好推)
对于操作1,其实就是暴力拆,然后合并(因为 m e r g e ( a , b merge(a,b merge(a,b)要求 a , b a,b ab两个子树满足 a a a里的权值都小于 b b b中的权值,所以不能直接合并)


那么我们再来看看另外一种做法–线段树合并与分裂
其实主要需要讲的就是合并和分裂操作,其他都与普通的线段树操作一样(当然需要用到动态开点)

对于合并 m e r g e merge merge操作:
考虑合并 x , y x,y xy节点(默认新节点就是 x x x,当然有的题必须新开一个节点作为合并后的节点,因题而异)
我们直接将 y y y中维护的信息加到 x x x
然后递归操作:
t r [ x ] . l = m e r g e ( t r [ x ] . l , t r [ y ] . l ) ; tr[x].l=merge(tr[x].l,tr[y].l); tr[x].l=merge(tr[x].l,tr[y].l);
t r [ x ] . r = m e r g e ( t r [ x ] . r , t r [ y ] . r ) ; tr[x].r=merge(tr[x].r,tr[y].r); tr[x].r=merge(tr[x].r,tr[y].r);
然后将y节点回收(回收的意思就是把这个节点清空然后留给以后新建节点用,可以节省空间复杂度)

对于分裂 s p l i t split split操作
考虑将以 k k k为根节点的线段树分为两棵,按照某种标准分裂,此题是按照 n u m num num值分裂(也就是权值的个数),一棵仍然以 k k k为根节点,另一棵以 y y y为根节点
显然 y y y原来是不存在的,因此我们对他新建,如果 l s . n u m < n u m ls.num<num ls.num<num那么左子树仍然是 k k k的,递归右子树下去分裂即可,否则右子树一定是 y y y的,因此我们先给 y y y的右子树赋值,再递归左子树下去分裂即可,代码如下:

void split(int k,int &y,int num){
	y=build();$
	if(num>tr[tr[k].l].num) split(tr[k].r,tr[y].r,num-tr[tr[k].l].num);
	else swap(tr[k].r,tr[y].r);
	if(num<tr[tr[k].l].num) split(tr[k].l,tr[y].l,num);
	tr[y].num=tr[k].num-num,tr[k].num=num;
}

代码 无旋Treap

#include<bits/stdc++.h>
#define int long long
#define M 700009
using namespace std;
int read(){
	int f=1,re=0;char ch;
	for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
	if(ch=='-'){f=-1,ch=getchar();}
	for(;isdigit(ch);ch=getchar()) re=(re<<3)+(re<<1)+ch-'0';
	return re*f; 
}
int n,m,tot,rt[M],cnt=1;
const int mod=1e9+7;
struct tree{int l,r,num,siz,pos,val;}tr[M];
void pushup(int k){tr[k].siz=tr[tr[k].l].siz+tr[tr[k].r].siz+tr[k].num;}
int build(int val,int num){
	tr[++tot].val=val,tr[tot].siz=tr[tot].num=num,tr[tot].pos=rand();
	return tot;	
}
int merge(int x,int y){
	int p;
	if(!x||!y) return x+y;
	if(tr[x].pos<tr[y].pos) p=x,tr[p].r=merge(tr[p].r,y); 
	else p=y,tr[p].l=merge(x,tr[p].l);
	pushup(p);return p;
} 
void split(int x,int &a,int &b,int val){
	if(!x){a=b=0;return;}
	if(tr[x].val<=val) a=x,split(tr[a].r,tr[a].r,b,val);
	else b=x,split(tr[b].l,a,tr[b].l,val);
	pushup(x);
}
int kth(int k,int val){
	if(val>tr[k].siz) return -1; 
	if(tr[tr[k].l].siz+tr[k].num<val) return kth(tr[k].r,val-tr[tr[k].l].siz-tr[k].num);
	if(tr[tr[k].l].siz>=val) return kth(tr[k].l,val);
	return tr[k].val;
}
void insert(int &k,int val,int num){
	int x,y,z;
	split(k,x,y,val);
	split(x,x,z,val-1);
	if(tr[z].val==val) tr[z].num+=num,tr[z].siz+=num;
	else z=build(val,num);
	k=merge(merge(x,z),y); 
}
void dfs(int k,int &y){
	if(tr[k].l) dfs(tr[k].l,y);
	insert(y,tr[k].val,tr[k].num);
	if(tr[k].r) dfs(tr[k].r,y);
}
signed main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++){
		int x=read();
		rt[1]=merge(rt[1],build(i,x));
	}
	for(int i=1;i<=m;i++){
		int opt=read(),p=read(),x,y,z,w,v;
		if(opt==0) x=read(),y=read(),split(rt[p],z,rt[++cnt],x-1),split(rt[cnt],rt[cnt],w,y),rt[p]=merge(z,w);
		if(opt==1) x=read(),dfs(rt[x],rt[p]);
		if(opt==2) x=read(),y=read(),insert(rt[p],y,x);
		if(opt==3) x=read(),y=read(),split(rt[p],z,w,x-1),split(w,w,v,y),printf("%lld\n",tr[w].siz),rt[p]=merge(z,merge(w,v));
		if(opt==4) x=read(),printf("%lld\n",kth(rt[p],x));
	}return 0;
} 

代码 线段树合并与分裂

#include<bits/stdc++.h>
#define int long long
#define M 100009
using namespace std;
int read(){
	int f=1,re=0;char ch;
	for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
	if(ch=='-'){f=-1,ch=getchar();}
	for(;isdigit(ch);ch=getchar()) re=(re<<3)+(re<<1)+ch-'0';
	return re*f;
}
int n,m,num1,tot,cnt=1,rec[M],rt[M];
struct tree{int l,r,num;}tr[M*32];
int build(){
	if(num1) return rec[num1--];
	else return ++tot;
}
void recyc(int x){rec[++num1]=x,tr[x].l=tr[x].r=tr[x].num=0;}
void update(int &k,int l,int r,int pos,int val){
	if(!k) k=build();tr[k].num+=val;
	if(l==r) return;
	int mid=(l+r)>>1;
	if(pos<=mid) update(tr[k].l,l,mid,pos,val);
	else update(tr[k].r,mid+1,r,pos,val);
}
int query(int k,int l,int r,int ql,int qr){
	if(l>=ql&&r<=qr) return tr[k].num;
	int mid=(l+r)>>1,ret=0;
	if(ql<=mid) ret+=query(tr[k].l,l,mid,ql,qr);
	if(qr>mid) ret+=query(tr[k].r,mid+1,r,ql,qr);
	return ret; 
}
void split(int k,int &y,int num){
	y=build();
	if(num>tr[tr[k].l].num) split(tr[k].r,tr[y].r,num-tr[tr[k].l].num);
	else swap(tr[k].r,tr[y].r);
	if(num<tr[tr[k].l].num) split(tr[k].l,tr[y].l,num);
	tr[y].num=tr[k].num-num,tr[k].num=num;
}
int merge(int x,int y){
	if(!x||!y) return x+y;
	tr[x].num+=tr[y].num;
	tr[x].l=merge(tr[x].l,tr[y].l);
	tr[x].r=merge(tr[x].r,tr[y].r);
	recyc(y);return x;
}
int kth(int k,int l,int r,int num){
	if(tr[k].num<num) return -1;
	if(l==r) return l;
	int mid=(l+r)>>1;
	if(tr[tr[k].l].num<num) return kth(tr[k].r,mid+1,r,num-tr[tr[k].l].num);
	else return kth(tr[k].l,l,mid,num);
}
signed main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++){
		int x=read();
		update(rt[1],1,n,i,x);
	}
	for(int i=1;i<=m;i++){
		int opt=read(),p=read(),x,y,z,w,v;
		if(opt==0) x=read(),y=read(),z=query(rt[p],1,n,1,y),w=query(rt[p],1,n,x,y),split(rt[p],rt[++cnt],z-w),split(rt[cnt],v,w),rt[p]=merge(rt[p],v);
		if(opt==1) x=read(),rt[p]=merge(rt[p],rt[x]);
		if(opt==2) x=read(),y=read(),update(rt[p],1,n,y,x);
		if(opt==3) x=read(),y=read(),printf("%lld\n",query(rt[p],1,n,x,y));
		if(opt==4) x=read(),printf("%lld\n",kth(rt[p],1,n,x));
	}return 0;
} 
学习笔记
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值