首页 > 其他分享 >线段树扩展学习

线段树扩展学习

时间:2024-07-24 16:31:48浏览次数:14  
标签:lc int 线段 扩展 学习 add rc 节点

前言

来补一下暑期集训的坑。

正文

标记可持久化

这个很好理解,在进行区间修改的时候,不下传懒标记,查询的时候直接对每一层再进行处理即可。

这个主要用于线段树分治,所以理解就行,代码不给了。

动态开点

之前一直不太会,现在来补一下。

这种线段树主要用于优化空间复杂度。就是对于下标,不用常规方法存储节点的左右儿子,而用一个单独的变量 \(tot\) 来存储。

这点大家作为资深 OIER 一定都懂,下面说一下代码方面和普通线段树的区别。

在写函数的时候,需要额外定义当前节点所代表的区间的信息。

本人喜欢用结构体存储,所以需要在结构体里面增加 \(lc,rc\) 代表儿子下标,并且用宏定义优化代码长度。在调用的时候将 \(t_{p*2}\) 改为 \(t_{lc(p)}\) 即可,右儿子同理。

懒标记部分判断一下 \(lc,rc\) 是否被赋值,如果没有让 \(tot++\) 然后再去赋值就行了。

区修部分需要额外开一个变量 \(p\) 来表示当前节点编号,函数里面用实参调用 \(p\),这样方便对 \(lc,rc\) 进行赋值,其余不变。

查询部分只需要在最开始判断一下当前编号是否已经被赋值,如果没有直接返回。这也是动态开点线段树优化空间的直接表现。

稍微放下洛谷线段树模板一的代码,最开始的时候忘记 \(upd\) 了,查了好久才发现我太菜了

点击查看代码
#include<bits/stdc++.h>
#define int long long
const int maxn=1e6+10;
using namespace std;
inline int read()
{
	int w=1,s=0;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch)){s=s*10+(ch-'0');ch=getchar();}
	return w*s;
}
int n,Q,tot,st;
struct no
{
	int d,add;
	int lc,rc;
	#define lc(x) t[x].lc
	#define rc(x) t[x].rc
}t[maxn<<2];
void upd(int p)
{
	t[p].d=t[lc(p)].d+t[rc(p)].d;
}
void spread(int p,int tl,int tr)
{
	if(!t[p].add)return ;
	int mid=(tl+tr)>>1;
	if(!lc(p))t[p].lc=++tot;
	if(!rc(p))t[p].rc=++tot;
	t[lc(p)].d+=t[p].add*(mid-tl+1);
	t[rc(p)].d+=t[p].add*(tr-mid);
	t[lc(p)].add+=t[p].add;
	t[rc(p)].add+=t[p].add;
	t[p].add=0;
}
void add(int &p,int tl,int tr,int l,int r,int k)
{
	if(!p) p=++tot;
	if(tl>=l&&tr<=r)
	{
		t[p].d+=k*(tr-tl+1);
		t[p].add+=k;
		return ;
	}
	spread(p,tl,tr);
	int mid=(tl+tr)>>1;
	if(mid>=l)add(lc(p),tl,mid,l,r,k);
	if(mid<r)add(rc(p),mid+1,tr,l,r,k);
	upd(p);
}
int ask(int p,int tl,int tr,int l,int r)
{
	if(!p)return 0;
	if(tl>=l&&tr<=r)return t[p].d;
	spread(p,tl,tr);
	int mid=(tl+tr)>>1,ma=0;
	if(mid>=l)ma+=ask(lc(p),tl,mid,l,r);
	if(mid<r)ma+=ask(rc(p),mid+1,tr,l,r);
	return ma;
}
signed main()
{
	cin>>n>>Q;
	for(int i=1;i<=n;i++)
	{
		int x=read();
		add(st,1,n,i,i,x);
	}
	while(Q--)
	{
		int opt=read(),l=read(),r=read();
		if(opt==1){int k=read();add(st,1,n,l,r,k);}
		else printf("%lld\n",ask(st,1,n,l,r));
	}
	return 0;
}

线段树合并

这个东西找不到模板题目,所以就讲一下部分实现吧。

其实很简单,对两个线段树维护的信息进行合并,需要更新下标和维护内容。

下标的话只需要特判一下是否存在,然后不存在的话新开一个节点就行,维护内容的话就正常处理。

代码很短,不放了。

线段树优化建图

这个比前面的稍微难一点,不过也还行。

放一道模板题

考虑建图的时候如果需要让一个节点和一个区间内的所有节点连边,那么正常情况下会爆时间。

所以我们把连边操作放到线段树里面。连边时,我们只需要让节点对对应区间连边即可。

但是如果有无向边的话,一颗线段树会乱套,所以我们建两棵线段树,分别存储点连向线段树区间节点和线段树区间结点连向点的边。下文我们称它们分别为入树和出树。

最开始的时候,我们让入树中左右节点连向它的两个儿子,出树则让儿子连向它的父亲节点,都是有向边,边权为 \(0\)。同时让两棵树对应位置的所有叶节点也连上边权为 \(0\) 的无向边,这样两棵线段树就完成了初始化。

对于编号的话,我们让入树的节点编号正常搞,出树则根据数据范围加上一个偏移量 \(k\)。对于所有叶节点,我们用一个数组 \(id\) 表示原数组中的位置在入树中所对应的叶节点编号,然后对于出树,叶节点编号直接就是 \(id_i+k\) 了。

以点连向区间为例,我们让出树中对应的叶节点连向入树中覆盖该区间的节点,借用一下 maoyiting 大佬的图
image
另一种操作同理。

模板题要求跑单源最短路,那么我们找到起点在出树中所对的编号,然后正常 dijkstra 即可。

点击查看代码
正在写,别急。

主席树

明天再更。

标签:lc,int,线段,扩展,学习,add,rc,节点
From: https://www.cnblogs.com/Lydic/p/18320928

相关文章

  • MySQL 学习笔记 基础(多表查询下,事务)
    多表查询 多表查询-子查询概念:SQL语句中嵌套SELECT语句,称为嵌套语句,又称子查询。SELECT*FROMt1WHEREcolumn=(SELECTcolumn1FROMt2);子查询外部的语句可以是INSERT/UPDATE/DELETE/SELECT的任何一个。根据子查询结果不同,分为:·标量子查询......
  • Java学习笔记(三)算术运算符、逻辑运算符、四种进制介绍
    Hii,mJinXiang⭐前言⭐本篇文章主要介绍Java算术运算符、逻辑运算符、四种进制介绍详细使用以及部分理论知识......
  • Java学习笔记(七)面向对象编程(中级部分)
    Hii,mJinXiang⭐前言⭐本篇文章主要介绍Java面向对象编程(中级部分)包、访问修饰符、封装、继承、super关键字、多态、向上(下)转型、equals、hashCode、断点调试等知识的详细使用以及部分理论知识......
  • 学习Java的日子 Day56 数据库连接池,Druid连接池
    Day561.数据库连接池理解:池就是容器,容器中存放了多个连接对象使用原因:1.优化创建和销毁连接的时间(在项目启动时创建连接池,项目销毁时关闭连接池)2.提高连接对象的复用率3.有效控制项目中连接的个数(连接对象占内存资源)数据库连接池负责分配、管理和释放数据库连接......
  • 机器学习基础
    目录机器学习开发流程一、特征工程1.数据获取以鸢尾花为例2.特征抽取字典特征提取文本特征提取普通文本特征提取jieba分词TF-IDF重要程度3.特征编码4.特征预处理归一化标准化5.特征降维二、分类算法KNN算法knn算法实现模型选择与调优案例:facebook签到位置朴素贝叶斯决策树三、回......
  • OI-Wiki 学习笔记
    算法基础\(\text{Update:2024-07-22}\)复杂度定义衡量一个算法的快慢,一定要考虑数据规模的大小。一般来说,数据规模越大,算法的用时就越长。而在算法竞赛中,我们衡量一个算法的效率时,最重要的不是看它在某个数据规模下的用时,而是看它的用时随数据规模而增长的趋势,即时间复......
  • Android MVP模型的学习与使用
    一、MVP(Model-View-Presenter)概叙MVP(Model-View-Presenter)是一种用于Android应用程序开发的架构模式,旨在将应用程序的不同部分分离,以提高代码的可维护性和可测试性。MVP模型包含三个主要组成部分:Model、View和Presenter。Model(模型):​ Model负责处理应用程序的数据和业务逻......
  • 线段树(区间操作,例题:洛谷P3372 线段树 1)
    在上一节线段树(原理、构造和区间查询,例题:BalancedLineup)中介绍了线段树的构造,下面就来说一下它的区间操作。区间操作与Lazy-Tag有关,如果修改操作是对区间内的每个元素一一修改,就会比较繁琐低效,目前的解决办法是线段树的tree[i].data记录的是区间i的值(详细见上节),可以再定义一......
  • 逆向分析学习入门教程(非常详细)零基础入门到精通,看这一篇就够了!_逆向都要学啥
    前沿从本篇起,逆向工厂带大家从程序起源讲起,领略计算机程序逆向技术,了解程序的运行机制,逆向通用技术手段和软件保护技术,更加深入地去探索逆向的魅力。一、程序如何诞生?1951年4月开始在英国牛津郡哈维尔原子能研究基地正式投入使用的英国数字计算机“哈维尔·德卡特伦”,是......
  • 哈夫曼树学习笔记
    哈夫曼树学习笔记定义设二叉树具有\(n\)个带权叶结点,从根结点到各叶结点的路径长度与相应叶节点权值的乘积之和称为树的带权路径长度(WeightedPathLengthofTree,WPL)。设\(w_i\)为二叉树第\(i\)个叶结点的权值,\(l_i\)为从根结点到第\(i\)个叶结点的路径长度,则WPL......