首页 > 其他分享 >线段树合并学习笔记

线段树合并学习笔记

时间:2023-01-12 21:23:07浏览次数:44  
标签:rt cur int tree 线段 合并 mid 笔记 lt

前置芝士:线段树动态开点

使用场景:

  • 维护区间太大,\(4\times N\) 存不下,通常是值域线段树。
  • 维护的区间下标存在负数。

空间复杂度:

  • 全部开点,则 \(2\times N -1\)
  • 每递归一次,最多开点 \(O(\log N)\),若调用 \(M\) 次就是 \(M\log N\)。

原理:

  • 若一段子区间 \([L,R]\) 对应的线段树节点为 \(cur\),当不需要递归时,就不建点。
  • 当调用 addtag() 时,新建节点。

注意事项:

  • 没有 build() 的过程。都让你动态开点了怎么可能要 build() 啊喂
  • 区间存在负数时,\(mid\) 应是 lt+rt-1>>1 而不是 lt+rt>>1。(虽然正整数写这个也没关系)
  • addtag() 的 \(cur\) 加上取址符。
  • 根节点占一个,所以总结点数 \(tot\) 的初始值为 \(1\)。
模板代码(P3372)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=100005;
int n,m,tot;
int a[N];
#define lc(x) tree[x].lc
#define rc(x) tree[x].rc
#define sum(x) tree[x].val
#define tag(x) tree[x].tag
struct node{
	int val,tag,lc,rc;
}tree[N<<1]; 
void push_up(int cur){
	tree[cur].val=tree[lc(cur)].val+tree[rc(cur)].val;
	return;
}
void addtag(int &cur,int lt,int rt,int val){
	if(!cur)cur=++tot;
	tree[cur].tag+=val;
	tree[cur].val+=(rt-lt+1)*val;
	return;
}
void push_down(int cur,int lt,int rt){
	if(lt>=rt)return;
	int mid=(lt+rt-1)>>1;
	addtag(lc(cur),lt,mid,tree[cur].tag);
	addtag(rc(cur),mid+1,rt,tree[cur].tag);
	tree[cur].tag=0;
	return;
}
int query(int cur,int lt,int rt,int qx,int qy){
	if(lt>qy||rt<qx)return 0;
	if(qx<=lt&&qy>=rt)return tree[cur].val;
	push_down(cur,lt,rt);
	int mid=(lt+rt-1)>>1;
	return query(lc(cur),lt,mid,qx,qy)+query(rc(cur),mid+1,rt,qx,qy);
}
void update(int cur,int lt,int rt,int qx,int qy,int val){
	if(lt>qy||rt<qx)return;
	if(qx<=lt&&qy>=rt){
		addtag(cur,lt,rt,val);
		return;
	}
	int mid=(lt+rt-1)>>1;
	push_down(cur,lt,rt);
	update(lc(cur),lt,mid,qx,qy,val);
	update(rc(cur),mid+1,rt,qx,qy,val);
	push_up(cur);
	return;
}
signed main(){
	cin>>n>>m;
	tot=1;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		update(1,1,n,i,i,a[i]);
	}
	for(int i=1;i<=m;i++){
		int kind;
		cin>>kind;
		if(kind==1){
			int x,y,k;
			cin>>x>>y>>k;
			update(1,1,n,x,y,k);
		}
		else{
			int x,y;
			cin>>x>>y;
			cout<<query(1,1,n,x,y)<<'\n';
		}
	}
	return 0;
}

正题:线段树合并

使用场景:有多棵线段树,维护了相同的区间 \([L,R]\),通常是权值线段树。每一棵线段树维护了区间内的最大值(区间元素和),\(M\) 次单点修改,每次修改一棵线段树的位置为 \(pos\) 的值,修改后所有线段树对应区间位置的权值相加并维护区间最大值。

板子
int merge(int a,int b,int l,int r){
	if(!a||!b)return a+b;
	if(l==r){
		tree[a].val+=tree[b].val;
		tree[a].pos=tree[a].val?l:0;
	}
	int mid=lt+rt>>1;
	lc(a)=merge(lc(a),lc(b),l,mid);
	rc(a)=merge(rc(a),rc(b),mid+1,r);
	push_up(a,l,r);;
	return a;
}

标签:rt,cur,int,tree,线段,合并,mid,笔记,lt
From: https://www.cnblogs.com/Forever1507/p/17047942.html

相关文章

  • Hadoop学习笔记01
    一、大数据概念大数据​大数据(BigData):指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合。主要解决问题海量数据的采集存储和分析......
  • 《Vue.js 设计与实现》读书笔记(1-3章)
    第1章、权衡的艺术命令式or声明式命令式:关注过程声明式:关注结果声明式直接声明想要的结果,框架帮用户封装好命令式的代码,所以在封装的过程中要做一些其他的事情来(生......
  • 莫比乌斯反演学习笔记
    莫比乌斯函数定义\[\mu(n)=\begin{cases}1&n=1\\0&n\text{含有平方因子}\\(-1)^k&\text{其中}k\text{为}n\text{本质不同的质因子个数}\end{cases}......
  • 学习笔记——Mybatis动态SQL
    2023-01-12一、Mybatis动态SQL即将SQL动态化同时Mybatis的动态SQL支持OFNL表达式,OGNL(ObjectGraphNavigationLanguage)对象图导航语言。1、先搭建环境(1)创建一个“mav......
  • Redis 6 学习笔记1 ——NoSQL数据库介绍,Redis常用数据类型
    NoSQL数据库介绍(了解)技术的分类1、解决功能性的问题:Java、Jsp、RDBMS、Tomcat、HTML、Linux、JDBC、SVN,2、进一步地,解决系统功能扩展性的问题:Struts、Spring、SpringMVC......
  • ASP.NET Core学习笔记3
    ASP.NETCore学习笔记3      结论:n AmbiguousHTTPmethodforaction,翻译后是“不明确的HTTP操作方法”。n 有可能是没写HTTP方法,如[HttpGet]、......
  • Math学习笔记
    最近几天全在做OI数论题,写个blog记一下套路。例如要求\(\operatornameg(n)=\sum_{d|n}d\cdot\varphi(\frac{n}{d})\)尽管你会一个叫做\(\text{LCMSUM}\)(可跳转)......
  • 【线段树】动态开点
    使用场景维护的区间太大以至于\(4N\)存不下,通常是权值线段树;维护的区间下标存在负数;时间复杂度全部开点,则\(O(2N-1)\)每递归一次,最多开点\(O(\log_N)\),若调......
  • Matplotlib学习笔记1 - 上手制作一些图表吧!
    Matplotlib学习笔记1-上手制作一些图表吧!Matplotlib是一个面向Python的,专注于数据可视化的模块。快速上手这是使用频率最高的几个模块,在接下来的程序中,都需要把它们作......
  • 算法学习笔记(3): 倍增与ST算法
    倍增目录倍增查找洛谷P2249重点变式练习快速幂ST表扩展-运算扩展-区间变式答案倍增,字面意思即”成倍增长“他与二分十分类似,都是基于”2“的划分思想那么具体是怎......