浅谈线段树 Segment Tree
众所周知,线段树是algo中很重要的一项!
一.简介
线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,实际应用时一般还要开4N的数组以免越界,因此有时需要离散化让空间压缩。
二.用途
单点 : 查询(query)修改(add,mul)
区间 : 查询(区间和),修改,最大值(max),最小值(min)。
三. 实现方式
1.建树
由于每个点都表示一个区间,所以他有很多信息(左儿子,右儿子,区间sum) 所以我们用结构体存. 因为之后要用到懒标记,所以结构体还有两个懒标记。
懒标记 : 以上图为例,如果想在1 - 6区间内加一,我们就将这个信息从根节点传递到下一层,这时2,3点都有一个add = 1的懒标记,这样就表示已经加过1了,下次如果还要加,那么直接加在懒标记上。就比如你挣了一笔钱,暂时不用,就存在银行里了。之后如果求解需要递归,那么这个懒标记就向下传,并且传完后自己要清零!(这样更新后的状态就是 原状态 + 子区间点的个数 * 传下里的懒标记,(example sum = 5(原状态)+ 4(区间里有4个数,都加了个2) * 2(懒标记))-------很玄学
乘法的懒标记(luogu p3373):需要特别注意下
比如 懒标记原本为2 + 3
现在传下一个乘8 那么就变为(2 + 3) * 8
然后再传一个加三,就会变成(2 + 3 + 3) * 8
所以我们这么存 2 * 8 + 3 * 8
这样加3后值才是正确的!
上代码
代码中% P 为题目要求
struct Node {
int l, r;
ll sum;
ll add, mul; Node() {
l = r = sum = add = ;
mul = ;
} void update_add(ll value) {
add = (add + value) % P;
sum = (sum + (r - l + ) * value) % P;
} void update_mul(ll value) {
sum = (sum * value) % P;
mul = (mul * value) % P;
add = (add * value) % P;
}
} t[N << ];
我的建树可能比较怪,当递归到根节点再cin,一边递归一边更新(push_up,后面有)
void build_tree(int p, int l, int r) {
t[p].l = l, t[p].r = r;
if (l == r) {
cin >> t[p].sum;
return;
}
int mid = (t[p].l + t[p].r) >> ;
build_tree(lc(p), l, mid);
build_tree(rc(p), mid + , r);
push_up(p);
}
左儿子右儿子
inline int lc(int p) {
return p << ;
} inline int rc(int p) {
return p << | ;
}
向上push_up更新信息(sum),向下传懒标记(push_down) 切记传完后自己状态要恢复哦!
void push_up(int p) {
t[p].sum = t[lc(p)].sum + t[rc(p)].sum;
} void push_down(int p) {
if (t[p].mul != ) {
t[lc(p)].update_mul(t[p].mul);
t[rc(p)].update_mul(t[p].mul);
t[p].mul = ;
}
if (t[p].add) {
t[lc(p)].update_add(t[p].add);
t[rc(p)].update_add(t[p].add);
t[p].add = ;
}
}
Å%%%Then
当我们进行区间改动时
(黑色为总区间,红色为需要修改的区间)
如果当前区间是全部区间的子集————那很好,咱们可以直接修改
如果当前区间和总区间有交集,那么就递归,找到第一个完全包含他的区间,然后修改,再递归上去
上代码!!!
void update1(int p, int l, int r, ll value) {//乘法更新
if (t[p].l >= l && t[p].r <= r) {
t[p].update_mul(value);
return;
}
push_down(p);
int mid = (t[p].l + t[p].r) >> ;
if (l <= mid) update1(lc(p), l, r, value);
if (r > mid) update1(rc(p), l, r, value);
push_up(p);
} void update2(int p, int l, int r, ll value) {//加法更新
if (t[p].l >= l && t[p].r <= r) {
t[p].update_add(value);
return;
}
push_down(p);
int mid = (t[p].l + t[p].r) >> ;
if (l <= mid) update2(lc(p), l, r, value);
if (r > mid) update2(rc(p), l, r, value);
push_up(p);
} ll query(int p, int l, int r) {//区间查询,如果是单点差距的话l == r
if (t[p].l >= l && t[p].r <= r) {
return t[p].sum % P;
}
push_down(p);
ll sum = ;
int mid = (t[p].l + t[p].r) >> ;
if (l <= mid) sum = (sum + query(lc(p), l, r)) % P;
if (r > mid) sum = (sum + query(rc(p), l, r)) % P;
return sum % P;
}
当然还可以求RMQ问题
struct Node
{
ll minn,maxx;
}t[]; //build 里加几句
t[p].maxx = max(t[lc(p)].maxx,t[rp(p)].maxx);
t[p].minn = min(t[lc(p)].minn,t[rp(p)].minn); int ans1,ans2;
void new_query(int p,int l,int r)
{
if(t[p].l == l && t[p].r == r)
{
ans1 = max(ans1,t[p].maxx);
ans2 = max(ans2,t[p].minn);
return;
}
int mid = (t[p].l + t[p].r) >> ;
if(r <= mid)
query(lc(p),l,r);
else if (l > mid)
query(rc(p),l,r);
else
{
query(lc(p),l,mid);
query(rp(p),mid + ,r);
}
}
下面附上总代码(代码按照luogu 线段树2的模板打的,可AC)
#include <iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + ;
typedef long long ll; ll P; struct Node {
int l, r;
ll sum;
ll add, mul;
// ll minn,mmax;
Node() {
l = r = sum = add = ;
mul = ;
} void update_add(ll value) {
add = (add + value) % P;
sum = (sum + (r - l + ) * value) % P;
} void update_mul(ll value) {
sum = (sum * value) % P;
mul = (mul * value) % P;
add = (add * value) % P;
}
} t[N << ]; inline int lc(int p) {
return p << ;
} inline int rc(int p) {
return p << | ;
} void push_up(int p) {
t[p].sum = t[lc(p)].sum + t[rc(p)].sum;
} void push_down(int p) {
if (t[p].mul != ) {
t[lc(p)].update_mul(t[p].mul);
t[rc(p)].update_mul(t[p].mul);
t[p].mul = ;
}
if (t[p].add) {
t[lc(p)].update_add(t[p].add);
t[rc(p)].update_add(t[p].add);
t[p].add = ;
}
} void build_tree(int p, int l, int r) {
t[p].l = l, t[p].r = r;
if (l == r) {
cin >> t[p].sum;
return;
}
int mid = (t[p].l + t[p].r) >> ;
build_tree(lc(p), l, mid);
build_tree(rc(p), mid + , r);
// t[p].maxx = max(t[lc(p)].maxx,t[rp(p)].maxx);
// t[p].minn = min(t[lc(p)].minn,t[rp(p)].minn);
push_up(p);
} void update1(int p, int l, int r, ll value) {
if (t[p].l >= l && t[p].r <= r) {
t[p].update_mul(value);
return;
}
push_down(p);
int mid = (t[p].l + t[p].r) >> ;
if (l <= mid) update1(lc(p), l, r, value);
if (r > mid) update1(rc(p), l, r, value);
push_up(p);
} void update2(int p, int l, int r, ll value) {
if (t[p].l >= l && t[p].r <= r) {
t[p].update_add(value);
return;
}
push_down(p);
int mid = (t[p].l + t[p].r) >> ;
if (l <= mid) update2(lc(p), l, r, value);
if (r > mid) update2(rc(p), l, r, value);
push_up(p);
} ll query(int p, int l, int r) {
if (t[p].l >= l && t[p].r <= r) {
return t[p].sum % P;
}
push_down(p);
ll sum = ;
int mid = (t[p].l + t[p].r) >> ;
if (l <= mid) sum = (sum + query(lc(p), l, r)) % P;
if (r > mid) sum = (sum + query(rc(p), l, r)) % P;
return sum % P;
}
/*int ans1,ans2;
void new_query(int p,int l,int r)
{
if(t[p].l == l && t[p].r == r)
{
ans1 = max(ans1,t[p].maxx);
ans2 = max(ans2,t[p].minn);
return;
}
int mid = (t[p].l + t[p].r) >> 1;
if(r <= mid)
new_query(lc(p),l,r);
else if (l > mid)
new_query(rc(p),l,r);
else
{
new_query(lc(p),l,mid);
new_query(rp(p),mid + 1,r);
}
}
*/ int main()
{
int n, m;
cin >> n >> m >> P;
build_tree(, , n);
while (m--) {
int op, l, r, num;
cin >> op >> l >> r;
if (op == || op == ) cin >> num;
if (op == ) update1(, l, r, num);
else if (op == ) update2(, l, r, num);
else cout << query(, l, r) << endl;
}
} //Juddav007 0.0
(all)
THANKS FOR WATCHING!
浅谈线段树 Segment Tree的更多相关文章
- 『线段树 Segment Tree』
更新了基础部分 更新了\(lazytag\)标记的讲解 线段树 Segment Tree 今天来讲一下经典的线段树. 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间 ...
- 线段树(Segment Tree)(转)
原文链接:线段树(Segment Tree) 1.概述 线段树,也叫区间树,是一个完全二叉树,它在各个节点保存一条线段(即“子数组”),因而常用于解决数列维护问题,基本能保证每个操作的复杂度为O(lg ...
- 【转】Senior Data Structure · 浅谈线段树(Segment Tree)
本文章转自洛谷 原作者: _皎月半洒花 一.简介线段树 ps: _此处以询问区间和为例.实际上线段树可以处理很多符合结合律的操作.(比如说加法,a[1]+a[2]+a[3]+a[4]=(a[1]+a[ ...
- BZOJ.4695.最假女选手(线段树 Segment tree Beats!)
题目链接 区间取\(\max,\ \min\)并维护区间和是普通线段树无法处理的. 对于操作二,维护区间最小值\(mn\).最小值个数\(t\).严格次小值\(se\). 当\(mn\geq x\)时 ...
- 【数据结构系列】线段树(Segment Tree)
一.线段树的定义 线段树,又名区间树,是一种二叉搜索树. 那么问题来了,啥是二叉搜索树呢? 对于一棵二叉树,若满足: ①它的左子树不空,则左子树上所有结点的值均小于它的根结点的值 ②若它的右子树不空, ...
- 线段树(segment tree)
线段树在一些acm题目中经常见到,这种数据结构主要应用在计算几何和地理信息系统中.下图就为一个线段树: (PS:可能你见过线段树的不同表示方式,但是都大同小异,根据自己的需要来建就行.) 1.线段树基 ...
- 浅谈线段树 (例题:[USACO08FEB]酒店Hotel)By cellur925
今天我们说说线段树. 我个人还是非常欣赏这种数据结构的.(逃)因为它足够优美,有递归结构,有左子树和右子树,还有二分的思想. emm这个文章打算自用,就不写那些基本的操作了... 1° 简单的懒标记( ...
- 线段树 Interval Tree
一.线段树 线段树既是线段也是树,并且是一棵二叉树,每个结点是一条线段,每条线段的左右儿子线段分别是该线段的左半和右半区间,递归定义之后就是一棵线段树. 例题:给定N条线段,{[2, 5], [4, ...
- (转)浅谈trie树
浅谈Trie树(字典树) Trie树(字典树) 一.引入 字典是干啥的?查找字的. 字典树自然也是起查找作用的.查找的是啥?单词. 看以下几个题: 1.给出n个单词和m个询问,每次询问 ...
随机推荐
- 使用Consul做leader选举的方案
在分布式集群部署模式下,为了维护数据一致性,通常需要选举出一个leader来进行协调,并且在leader挂掉后能从集群中选举出一个新的leader.选举leader的方案有很多种,对Paxos和Raf ...
- java自学小段 产生随机数
public class Suijishu { public static void main(String[] args) { double i=Math.random();//产生一个0-0.5的 ...
- Linux虚拟机--进入MySQL报错的解决办法
在Linux安装MySQL有时候会出现 [mysql]ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/ ...
- Gradle 梳理:安装、入门使用方法
Gradle 教程:第一部分,安装[翻译] 原文地址:http://rominirani.com/2014/07/28/gradle-tutorial-part-1-installation-se ...
- Fresco添加HTTP请求头
项目中用Fresco来管理图片由于服务器图片有不同的版本需要根据客户端的屏幕密度来选择不同的图片共享一份用OkHttp下载图片并添加HTTP头代码. public class OkHttpNetwor ...
- 微信小程序开发实战-天气小程序
园龄6年8个月了,还一篇文章都没写过,惭愧! 最近周末做了个天气预报小程序,在这里整理一下开发过程和注意点,给对小程序开发感兴趣的伙伴们提供点参考. 废话不多说,先上图最终效果: 下面进入正文: 第一 ...
- 【Spring Boot源码分析】@EnableAutoConfiguration注解(一)@AutoConfigurationImportSelector注解的处理
Java及Spring Boot新手,首次尝试源码分析,欢迎指正! 一.概述 @EnableAutoConfiguration注解是Spring Boot中配置自动装载的总开关.本文将从@Enable ...
- Redis分布式锁的一点小理解
1.在分布式系统中,我们使用锁机制只能保证同一个JVM中一次只有一个线程访问,但是在分布式的系统中锁就不起作用了,这时候就要用到分布式锁(有多种,这里指 redis) 2.在 redis当中可以使用命 ...
- Future类型的连锁的运用
Future类型的连锁的运用: 洗脸 刷牙 吃早饭 看电视 出门 串行: import scala.concurrent.{Await, Future} import scala.util.{Fail ...
- HTML input 文本框输入中文逗号自动转换为英文逗号
input 标签中增加 onkeyup.onafterpaste 属性: <input type="text"placeholder="中文逗号自动转换为英文逗号& ...