首页 > 其他分享 >线段树进阶-分裂合并

线段树进阶-分裂合并

时间:2023-08-16 17:36:04浏览次数:47  
标签:rt return 进阶 val int 线段 son 分裂 now

前置知识

动态开点权值线段树

相信各位都会

线段树合并

我们考虑对于两棵权值线段树,由于动态开点的缘故,这两棵树都是不满的

我们考虑能不能把这两棵树所保存的信息合并在一起

我们考虑这么一件事就是说,由于树不满,我们可以暴力扫

分为三种情况(设把 \(b\) 所在树并到 \(a\) 内,\(a\) 和 \(b\) 为两棵树中位置对应的节点)

  • 若扫到节点 \(now\) 时 \(a\) 或 \(b\) 为 \(0\) 也就是说是空节点,则返回 \(a+b\) (也就是非空那个,或者两个都空)

  • 否则把 \(val_a\to val_a+val_b\) 然后递归左右儿子,回溯回来后删除 \(b\)

然后 \(\dots\) 没了

是的就是这么敷衍

int merge(int a,int b){//线段树合并 
	if(!a or !b) return a+b;
	val[a]+=val[b];
	son[a][0]=merge(son[a][0],son[b][0]);
	son[a][1]=merge(son[a][1],son[b][1]);
	del(b);
	return a;
}

线段树分裂

什么fhq

于是我们真的类比 fhq

考虑把前 \(k\) 个和后面的分裂成两棵树

等会,前 \(k\) 个啥啊

前 \(k\) 个叶子节点,是的分裂标准是叶子节点

我们考虑如何分裂

如果左儿子权值大于等于 \(k\) 那么直接把整个右子树都送给裂出去的树

如果左儿子权值小于那么递归右子树,\(k\) 减去左子树权值

如果左子树大递归左子树 \(k\) 不变

void split(int x,int &y,T k){//分裂 
	if(!x) return;
	y=nnd();
	T v=val[son[x][0]];
	if(k>v){
		split(son[x][1],son[y][1],k-v);
	}
	else swap(son[x][1],son[y][1]);
	if(k<v){
		split(son[x][0],son[y][0],k);
	}
	val[y]=val[x]-k;
	val[x]=k;
} 

按照我们的设想左子树权值为 \(k\)

例题\(\color{yellow}线段树分裂\)

代码

#include<bits/stdc++.h>
using namespace std;
const int N=4e5+5;
typedef long long ll;
template<typename T>
struct QZ_seg_tr{//权值线段树(带分裂合并) 
	int rt[N];
	T val[N*20];
	int tot=1,cnt;
	int son[N*20][2];
	int pol[N*20],dcnt;//这是一个特别的优化,开一个内存池把删掉的点的编号存下来循环利用 
	#define ls (son[now][0])
	#define rs (son[now][1])
	//适当宏增加可读性
	int nnd(){
		return dcnt?pol[dcnt--]:++cnt;
	} 
	void del(int now){
		pol[++dcnt]=now;
		ls=rs=val[now]=0;
	}
	//新建和删除节点
	void modi(int &now,int l,int r,int p,int v){//单点修改 
		if(!now){
			now=nnd();
		}
		val[now]+=v;
		if(l==r) return;
		int mid=(l+r)>>1;
		if(p<=mid) modi(ls,l,mid,p,v);
		else modi(rs,mid+1,r,p,v);
	}
	T query(int now,int l,int r,int L,int R){
		if(R<l or r<L) return 0;
		if(L<=l and R>=r){
			return val[now];
		}
		int mid=(l+r)>>1;
		return query(ls,l,mid,L,R)+query(rs,mid+1,r,L,R);
	}
	int qk(int now,int l,int r,int v){//查询区间比它小的数的个数 
		if(l==r) return l;
		int mid=(l+r)>>1;
		if(val[ls]>=v) return qk(ls,l,mid,v);
		else return qk(rs,mid+1,r,v-val[ls]);
	}
	int merge(int a,int b){//线段树合并 
		if(!a or !b) return a+b;
		val[a]+=val[b];
		son[a][0]=merge(son[a][0],son[b][0]);
		son[a][1]=merge(son[a][1],son[b][1]);
		del(b);
		return a;
	}
	void split(int x,int &y,T k){//分裂 
		if(!x) return;
		y=nnd();
		T v=val[son[x][0]];
		if(k>v){
			split(son[x][1],son[y][1],k-v);
		}
		else swap(son[x][1],son[y][1]);
		if(k<v){
			split(son[x][0],son[y][0],k);
		}
		val[y]=val[x]-k;
		val[x]=k;
	} 
};
QZ_seg_tr<long long> t;
int n,m;
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		int x;
		cin>>x;
		t.modi(t.rt[1],1,n,i,x);
	}
	while(m--){
		int opt;cin>>opt;
		int x,y,z;
		if(!opt){
			cin>>x>>y>>z;
			ll q1=t.query(t.rt[x],1,n,1,z);
			ll q2=t.query(t.rt[x],1,n,y,z);
			int A=0;
			t.split(t.rt[x],t.rt[++t.tot],q1-q2);
			t.split(t.rt[t.tot],A,q2);
			t.rt[x]=t.merge(t.rt[x],A);
		}
		else if(opt==1){
			cin>>x>>y;
			t.rt[x]=t.merge(t.rt[x],t.rt[y]);
		}
		else if(opt==2){
			cin>>x>>y>>z;
			t.modi(t.rt[x],1,n,z,y);
		}
		else if(opt==3){
			cin>>x>>y>>z;
			cout<<t.query(t.rt[x],1,n,y,z)<<"\n";
		}
		else{
			cin>>x>>y;
			if(t.val[t.rt[x]]<y) cout<<-1;
			else 
				cout<<t.qk(t.rt[x],1,n,y);
			cout<<"\n";
		}
	}
} 

标签:rt,return,进阶,val,int,线段,son,分裂,now
From: https://www.cnblogs.com/exut/p/17635558.html

相关文章

  • POJ 2828(线段树 单点更新)
    BuyTicketsTimeLimit: 4000MS MemoryLimit: 65536KTotalSubmissions: 16466 Accepted: 8201DescriptionRailwayticketsweredifficulttobuyaroundtheLunarNewYearinChina,sowemustgetupearlyandjoinalongqueue…TheLunarNewYearwasap......
  • ZOJ 3911 线段树(区间更新)
    PrimeQueryTimeLimit: 1Second     MemoryLimit: 196608KBYouaregivenasimpletask.Givenasequence A[i] with NHerearetheoperations:Avl,addthevalue v toelementwithindex l.(1<=V<=1000)Ralr,replacealltheelementsofse......
  • HDU 4893(线段树区间更新)
    Wow!SuchSequence!TimeLimit:10000/5000MS(Java/Others)    MemoryLimit:65536/65536K(Java/Others)TotalSubmission(s):3856    AcceptedSubmission(s):1085ProblemDescriptionRecently,Dogegotafunnybirthdaypresentfromhisnewf......
  • HDU 1698(线段树 区间更新)
    JustaHookTimeLimit:4000/2000MS(Java/Others)    MemoryLimit:32768/32768K(Java/Others)TotalSubmission(s):23481    AcceptedSubmission(s):11758ProblemDescriptionInthegameofDotA,Pudge’smeathookisactuallythemosthorr......
  • Vue进阶(幺伍贰):el-table-column :key 应用
    (文章目录)一、前言在前端项目开发过程中,el-table展示的结果列使用组件形式引入,其中某些字段通过:formatter方法转码,结果栏位的字段显示/隐藏控制也使用组件形式引入,前端在控制字段显示属性时,发现码值转换及字段信息展示均有问题。二、问题分析通过阅读代码结构,发现el-table-co......
  • salesforce零基础学习(一百三十)Report 学习进阶篇
    本篇参考:https://help.salesforce.com/s/articleView?id=sf.reports_summary_functions_about.htm&type=5https://www.youtube.com/watch?v=bjgZTgYe_84在SalesforceAdmin篇(二)Report中,我们讲过report的一些基础知识,实际工作中往往有些场景比这些复杂很多,接下来根据实际工作......
  • Kafka从入门到精通零基础进阶学习路线?
    Kafka从入门到精通零基础进阶学习路线?1.学习基础概念和架构:-了解Kafka的基础概念,如生产者、消费者、主题、分区等。-理解Kafka的架构,包括Kafkabroker、Zookeeper、消费者群组等。2.安装和配置Kafka:-下载和安装Kafka。-配置Kafkabroker和Zookeeper。3.发送......
  • go 进阶训练营 微服务可用性(中)笔记
    过载保护令牌桶算法存放固定容量令牌的桶,按照固定速率往桶里添加令牌https://pkg.go.dev/golang.org/x/time/rate漏桶算法作为计量工具(TheLeakyBucketAlgorithmasaMeter)时,可以用于流量整形(TrafficShaping)和流量控制(TrafficPolicing)https://pkg.go.dev/go.uber.......
  • Mybatis--进阶
    MyBatis--2.进阶MyBatis的Dao层实现传统开发方式Dao中的接口类:publicinterfaceUserMapper{publicList<User>findAll()throwsIOException;}Dao中接口的实现类:publicclassUserMapperImplimplementsUserMapper{@OverridepublicList<User>findA......
  • linux进阶:设备号
    设计字符设备文件系统调用系统IO的内核处理过程在Linux文件系统管理中,当应用程序调用open函数时,内核会根据文件路径找到文件的索引结点(inode),为文件分配文件描述符和文件对象,并根据打开模式和权限等参数进行相应的操作和设置。  硬件层原理思路:把底层寄存器配置操作放在文......