【学习笔记】线段树分裂

news/2024/9/21 4:26:00 标签: 学习, 笔记, 算法, 线段树分裂

前言

有线段树合并就应该有线段树分裂。它是线段树合并的逆过程。具体的,你需要以权值线段树中第 k 小的数为分界线,把线段树分成两半。

算法流程

和线段树上二分类似。假设原来的线段树为 u,要分裂出线段树 v

  1. 记左子树的权值为 val。
  2. 如果 k>val,那么分界线在右子树,那么左子树归 u,递归右子树,此时 k=k-val。
  3. 如果 k==val,那么分界线正好就是mid,那么左子树归 u,右子树归 v。
  4. 如果 k<val,那么分界线在左子树,那么右子树归 v,递归左子树
  5. 计算 u,v 的权值 tr[v].val=tr[u].val-k; tr[u].val=k;

核心代码

int split(int u,int v,int st,int ed,int k)
{
	if(u==0) return 0;
	int mid=st+ed>>1;
	tr.push_back(seg());
	v=tr.size()-1;
	int val=tr[tr[u].ls].val;
	if(k>val)
		tr[v].rs=split(tr[u].rs,tr[v].rs,mid+1,ed,k-val);
	else
		swap(tr[u].rs,tr[v].rs);
	if(k<val)
		tr[v].ls=split(tr[u].ls,tr[v].ls,st,mid,k);
	tr[v].val=tr[u].val-k;
	tr[u].val=k;
	return v;
}

【模板】线段树分裂

在这里插入图片描述
在这里插入图片描述

题解

操作0

先把线段树分裂 ( 1 , x − 1 ) , ( x , n ) (1,x-1),(x,n) (1,x1),(x,n),再把 ( x , n ) (x,n) (x,n) 线段树分裂 ( x , y ) , ( y + 1 , n ) (x,y),(y+1,n) (x,y),(y+1,n),最后合并线段树 ( 1 , x − 1 ) , ( y + 1 , n ) (1,x-1),(y+1,n) (1,x1),(y+1,n)

操作1

线段树合并

操作2

单点加

操作3

区间查

操作4

线段树上二分

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+7,inf=1e18;
int n,m;
struct seg
{
	int ls,rs,val;
	seg():ls(0),rs(0),val(0)
	{
	}
	seg(int a,int b,int c):ls(a),rs(b),val(c)
	{
	}
};
vector<seg> tr(2);
void update(int u)
{
	tr[u].val=tr[tr[u].ls].val+tr[tr[u].rs].val;
}
void insert(int u,int st,int ed,int x,int t)
{
	if(st==ed)
	{
		tr[u].val+=t;
		return;
	}
	int mid=st+ed>>1;
	if(x<=mid)
	{
		if(!tr[u].ls)
		{
			tr.push_back(seg());
			tr[u].ls=tr.size()-1;
		}
		insert(tr[u].ls,st,mid,x,t);
	}
	else
	{
		if(!tr[u].rs)
		{
			tr.push_back(seg());
			tr[u].rs=tr.size()-1;
		}
		insert(tr[u].rs,mid+1,ed,x,t);
	}
	update(u);
}
int query(int u,int st,int ed,int l,int r)
{
	if(l<=st&&ed<=r)
	{
		return tr[u].val;
	}
	int mid=st+ed>>1,res=0;
	if(l<=mid)
	{
		if(tr[u].ls)
			res+=query(tr[u].ls,st,mid,l,r);
	}
	if(mid<r)
	{
		if(tr[u].rs)
			res+=query(tr[u].rs,mid+1,ed,l,r);
	}
	return res;
}
void merge(int u,int v,int st,int ed)
{
	if(st==ed)
	{
		tr[u].val+=tr[v].val;
		return;
	}
	int mid=st+ed>>1;
	if(tr[u].ls&&tr[v].ls)
		merge(tr[u].ls,tr[v].ls,st,mid);
	else if(tr[v].ls)
		tr[u].ls=tr[v].ls;
	if(tr[u].rs&&tr[v].rs)
		merge(tr[u].rs,tr[v].rs,mid+1,ed);
	else if(tr[v].rs)
		tr[u].rs=tr[v].rs;
	update(u);
}
int split(int u,int v,int st,int ed,int k)
{
	if(u==0) return 0;
	int mid=st+ed>>1;
	tr.push_back(seg());
	v=tr.size()-1;
	int val=tr[tr[u].ls].val;
	if(k>val)
		tr[v].rs=split(tr[u].rs,tr[v].rs,mid+1,ed,k-val);
	else
		swap(tr[u].rs,tr[v].rs);
	if(k<val)
		tr[v].ls=split(tr[u].ls,tr[v].ls,st,mid,k);
	tr[v].val=tr[u].val-k;
	tr[u].val=k;
	return v;
}
int find(int u,int st,int ed,int k)
{
	if(k>tr[u].val||st>ed||k==0)
		return -1;
	if(st==ed)
	{
		return st;
	}
	int mid=st+ed>>1;
	int val=tr[tr[u].ls].val;
	if(k>val)
	{
		return find(tr[u].rs,mid+1,ed,k-val);
	}
	else
	{
		return find(tr[u].ls,st,mid,k);
	}
}
vector<int> rt(1);
void O_o()
{
	cin>>n>>m;
	rt.push_back(1);
	for(int i=1; i<=n; i++)
	{
		int x;
		cin>>x;
		insert(rt[1],1,n,i,x);
	}
	while(m--)
	{
		int op,id;
		cin>>op>>id;
		if(op==0)
		{
			int x,y;
			cin>>x>>y;
			rt.push_back(0);
			int now=rt.size()-1;
			int v1=query(rt[id],1,n,1,x-1),v2=query(rt[id],1,n,x,y);
			rt[now]=split(rt[id],rt[now],1,n,v1);
			int t=split(rt[now],0,1,n,v2);
			merge(rt[id],t,1,n);
		}
		else if(op==1)
		{
			int t;
			cin>>t;
			merge(rt[id],rt[t],1,n);
		}
		else if(op==2)
		{
			int x,q;
			cin>>x>>q;
			insert(rt[id],1,n,q,x);
		}
		else if(op==3)
		{
			int l,r;
			cin>>l>>r;
			cout<<query(rt[id],1,n,l,r)<<"\n";
		}
		else if(op==4)
		{
			int k;
			cin>>k;
			cout<<find(rt[id],1,n,k)<<"\n";
		}
		else assert(0);
	}
}
signed main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cout<<fixed<<setprecision(12);
	int T=1;
//	cin>>T;
	while(T--)
	{
		O_o();
	}
}

http://www.niftyadmin.cn/n/5668135.html

相关文章

Vue3:reactive丢失响应式,数据有更新但表单没有更新

出现问题&#xff1a;数据有更新但表单没有更新 使用reative定义了一个数组对象 let userHouseList reative<HouseListInter>([]) 在表单中使用 <el-table :data"userHouseList" stripe border style"width: 100%" height"500">…

Python Web 开发中的国际化与本地化处理

Python Web 开发中的国际化与本地化处理 目录 &#x1f30d; Flask中的国际化与本地化处理&#x1f310; Django中的国际化与本地化处理&#x1f5e3;️ 多语言支持与翻译系统实现&#x1f552; 时区和日期的本地化处理 1. &#x1f30d; Flask中的国际化与本地化处理 Flask…

文件批量添加水印和密码合并单元格完整版

这段代码是一个 Java 方法&#xff0c;用于向文件添加水印和密码。您解释一下&#xff1a; 首先&#xff0c;它接受一个 fileAddress 参数&#xff0c;表示文件的地址。 然后&#xff0c;它创建了一个线程安全的列表 fileDatas&#xff0c;用于存储文件数据。 接下来&#xff…

NFT Insider #148:The Sandbox 推出 SHIBUYA Y3K 时尚系列,Azuki 进军动漫 NFT 领域

市场数据 加密艺术及收藏品新闻 Infinex 新推 NFT 系列首四日销售额破4000万美元 尽管顶级 NFT 系列表现不佳&#xff0c;Infinex 的最新 NFT 系列在首四日内销售额已超过 4000 万美元。Infinex 是一个非托管平台&#xff0c;提供轻松访问链上协议和 dApp。 Infinex Core 的…

5G 扬帆新质跃,技术蝶变开新篇-第七届“绽放杯”5G应用征集大赛 5G应用融合技术专题赛圆满收官

2024年9月13日,由中国信息通信研究院、中国电信集团有限公司、中国移动通信集团有限公司、中国联合网络通信集团有限公司主办,5G应用产业方阵承办的第七届“绽放杯”5G应用征集大赛  5G应用融合技术专题赛决赛在深圳成功举办。 本次专题赛以“5G扬帆新质跃,技术蝶变开新篇”为…

Python学习的主要知识框架

Python的主要学习知识点非常广泛且深入&#xff0c;但我可以为您概括一些核心的学习领域&#xff0c;帮助您系统地掌握Python编程。以下是Python学习的主要知识框架&#xff1a; 1. Python基础语法 数据类型&#xff1a;整数、浮点数、字符串、布尔值、列表、元组、字典、集合…

Android文件系统的结构及目录用途、操作方法

Android文件系统的结构可以分为以下几个主要目录&#xff1a; /system&#xff1a;该目录包含Android操作系统核心文件&#xff0c;例如系统应用程序和库文件。一般情况下&#xff0c;此目录只能读取&#xff0c;无法写入。 /data&#xff1a;该目录用于存储应用程序的数据&am…

网站性能优化:如何高效预加载大型静态资源

网站性能优化&#xff1a;如何高效预加载大型静态资源 在开发中&#xff0c;面对大型文件&#xff0c;尤其是涉及炫酷动效的场景&#xff0c;如何高效加载资源是提升用户体验的关键。针对大文件的加载优化&#xff0c;推测性加载机制可以显著提高网页的响应速度和流畅度。本文…