首页 > 其他分享 >【模板】线段树

【模板】线段树

时间:2023-08-06 22:15:57浏览次数:33  
标签:lleft return int 线段 mid rright ll 模板

引入

题目描述

给定\(n\)个数\(a[1],a[2],a[3]...a[n]\),现在又下面两种操作:
1.询问区间\([x,y]\)的和,并输出。
2.将下标为\(x\)的数增加\(val\)。
一共\(x\)此操作
\(1\le n,m\le 100000\),保证在\(int\)范围内。

方法一:暴力枚举

定义数组\(a\)储存\(n\)个元素。
求区间和的时间复杂度为O(n),将\(a[x]\)增加\(val\)的时间复杂度为O(1),总时间复杂度为O(nm)
为什么超时?
因为每次求和的速度太慢了

前缀和

定义数组\(sum\),表示前缀和
求区间和的时间复杂度为O(1),将\(a[x]\)增加\(val\)的时间复杂度为O(n)
因为每一次进行增加操作,就需要更新所有的前缀和,所以总的时间复杂度为O(mn)

总结

所以,我们应该选取折中的方案

线段树

简介

将区间信息储存在树形结构中,也可以进行求解RMQ问题。
线段树解决了树状数组只能存储前缀信息和\(st\)表只能进行静态访问的问题。
线段树不光可以解决区间的前缀信息还可以求解区间的最值问题。
所以大部分可以用树状数组解决的问题都是可以用线段树解决的,但是这并不代表树状数组是没有意义的。
因为线段树的常数与码量较大,在有些时候并不是最优的选择。

基本思想

线段树其实本质上就是一棵完全二叉树,其根节点储存的就是原来的数据。
每个节点都有自己的左右端点,其中存储的数据就是答案。

单点修改线段树

以下代码求取的是区间最大值

建树

建树的操作使用了递归函数,传入的三个参数klr分别表示现在的节点,节点所表示的左端点与右端点。
如果左右两个端点是一样的就表示这是一个叶子节点,可以直接赋值并返回。
否则利用完全二叉树的性质进行递归寻找吗叶子节点。
在递归操作结束后将这个节点的两个子节点的最大值取出进行比较,其中的最大值就是这个区间的最大值。


void build(int k,int l,int r) {
	if(l==r){
		s[k]=a[l];
		return;
	}int mid=(l+r)/2;
	build(2*k,l,mid);
	build(2*k+1,mid+1,r);
	s[k]=max(s[k*2],s[k*2+1]);
    return ;
}

修改

add函数传入的参数表示现在的节点是k,左端点与右端点分别是lr
需要将第x个点修改为val


void add(int k,int l,int r,int x,int val) {
	if(l==r&&l==x){
		s[k]=val;
		return;
	}if(x<l||x>r)
		return;
	int mid=(l+r)/2;
	if(l<=x&&x<=mid)
		add(k*2,l,mid,x,val);
	if(mid+1<=x&&x<=r)
		add(k*2+1,mid+1,r,x,val);
	s[k]=max(s[k*2],s[k*2+1]);
    return ;
}

求最大值

sum函数传入的参数分别表示在第k个节点的左右端点分别为lr
xy这个区间中的最大值。


int sum(int k,int l,int r,int x,int y) {
	if(y<l||x>r)
		return 0;
	if(x<=l&&r<=y)
		return s[k];
	int mid=(l+r)/2;
	return max(sum(k*2,l,mid,x,y),sum(k*2+1,mid+1,r,x,y));
}

区间修改

区间修改的代码与单点修改大同小异,只是新增了一个lazy数组以提高时间复杂度
lazy数组完成的工作就是懒惰加载,只将需要访问的部分进行更改

处理lazy数组


void updata(ll k,ll lleft,ll rright){
	if(lazy[k]){
		lazy[k*2]+=lazy[k];
		lazy[k*2+1]+=lazy[k];
		ll mid=(lleft+rright-1)/2;
		s[k*2]+=lazy[k]*(mid-lleft+1);
		s[k*2+1]+=lazy[k]*(rright-mid);
		lazy[k]=0;
	}return ;
}

进行区间修改


void make(ll lleft,ll rright,ll l,ll r,ll val,ll x){
	if(l==lleft&&r==rright) {
		lazy[x]+=val;
		s[x]+=val*(r-l+1);
		return ;
	}if(l==r)
		return ;
	updata(x,l,r);
	ll mid=(l+r-1)>>1;
	if(rright<=mid)
		make(lleft,rright,l,mid,val,x<<1);
	else{
		if(lleft>mid)
			make(lleft,rright,mid+1,r,val,x*2+1);
		else{
			make(lleft,mid,l,mid,val,x*2);
			make(mid+1,rright,mid+1,r,val,x*2+1);
		}
	}s[x]=s[x<<1]+s[x*2+1];
	return ;
}

建树


void build(ll lleft,ll rright,ll x){
	if(lleft==rright){
		s[x]=a[lleft];
		return ;
	}ll mid=(lleft+rright-1)/2;
	build(lleft,mid,x*2);
	build(mid+1,rright,x*2+1);
	s[x]=s[x*2]+s[x*2+1];
	return ;
}

求区间和


ll sum(ll lleft,ll rright,ll l,ll r,ll k){
	if(lleft==l&&rright==r)
		return s[k];
	updata(k,l,r);
	ll mid=(l+r-1)/2;
	if(rright<=mid)
		return sum(lleft,rright,l,mid,k*2);
	if(lleft>mid)
		return sum(lleft,rright,mid+1,r,k*2+1);
	return sum(lleft,mid,l,mid,k*2)+sum(mid+1,rright,mid+1,r,k*2+1);
}

标签:lleft,return,int,线段,mid,rright,ll,模板
From: https://www.cnblogs.com/liudagou/p/17610158.html

相关文章

  • [Unity]URP HLSL Shader自用模板
    Shader"URP/falushan"{Properties//着色器的输入{_BaseMap("Texture",2D)="white"{}}SubShader{Tags{"RenderType"="Opaque""RenderPipeL......
  • 模板方法模式
    **模板方法模式:**在一个方法中定义一个算法的骨架,现将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。在模板模式(TemplatePattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象......
  • 【JavaScript09】模板字符串(Template Strings)
    前言JavaScript在ES6新增了模板字符串(TemplateStrings)语法,其作用是可以在字符串中换行,以及将变量和表达式插入字符串。模板字符串模板字面量使用反引号(``)而不是单引号('')或双引号("")来定义字符串示例:letuser="xwl";letage=20;letx=`myname......
  • C++_函数模板
    函数模板》是不进行编译的,因为类型还不知道模板的实例化》函数调用点进行实例化:在函数调用点,编译器用用户指定的类型,从原模板实例化一份函数代码出来模板函数》才是要被编译器所编译的模板类型参数typename/class模板非类型参数模板的实参推演》可以根据用户传......
  • 线段树
    线段树除了最后一层满二叉树,用堆(一维数组)来存树,一般来说,开4n的空间build(intu,intl,intr)将一段区间初始化为线段树pushup()由子节点更新父节点的信息pushdown()(懒标记)把信息递归的更新到两个子节点modify()/update()u为结点编号,更新该结点的区间最大值修改--单点修改......
  • Even(23Nowcoder6.J)(二分+可持久化线段树)
    题意:给定一个序列\(a\),定义一次操作选择序列中一个元素\(a[i]\),使\(a_i=\lfloor\frac{a_i}{2}\rfloor\),其中\(a_i\)为当前序列中的最大偶数,若没有则是最大奇数。有\(q\)组询问,每次给定\(k,l,r\)分别表示操作次数和操作区间,每次回答操作完成后区间中的\(Max\),询问间互相......
  • Django-4.2博客开发教程:初识模板(九)
    一、模板简介为了更好的维护和展示页面数据,使用直接返回数据显然是呆板的,不够美观,不够灵活,所以要使用模板。模板一般都放到项目根目录下的templates文件夹里。模板包含一些基础的HTML代码和一些特殊的语法,通过特殊的语法将数据动态的插入HTML页面中。特殊的语法中有一些变量......
  • 对拍模板使用教程
    对拍模板(Poweredby@tianbiandeshenghuo11)本模板基于CC0-1.0知识共享协议开源。模板下载地址:Link宣传:TBSHOJ注意,当前服务器即将到期。我们将会在不久后更新服务器。届时该网址将会失效。为您造成的不便,敬请谅解。模板使用教程:在下发模板中有几个文件。sol.cpp是暴......
  • P7771 【模板】欧拉路径
    \(P7771\)【模板】欧拉路径题目描述求有向图字典序最小的欧拉路径。输入格式第一行两个整数\(n,m\)表示有向图的点数和边数。接下来\(m\)行每行两个整数\(u,v\)表示存在一条\(u\tov\)的有向边。输出格式如果不存在欧拉路径,输出一行No。否则输出一行\(m+1\)个......
  • [Ynoi2012] NOIP2015 充满了希望(扫描线+线段树)
    题目传送门solution简单题。我们正着做扫描线。设\(t_i\)表示位置\(i\)最后一次进行二操作的时间,那么一操作就是交换\(t_x,t_y\),二操作就是区间复制。对于三操作,开一个树状数组,如果查询的位置的\(t_x=j\),就在\(j\)的位置上加一。查询就是查询后缀和。#include<bit......