浅谈线段树 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个询问,每次询问 ...
随机推荐
- 30 道 Vue 面试题
前言 本文以前端面试官的角度出发,对 Vue 框架中一些重要的特性.框架的原理以问题的形式进行整理汇总,意在帮助作者及读者自测下 Vue 掌握的程度. 本文章节结构以从易到难进行组织,建议读者按章节顺 ...
- [Pandas] 05 - Parallel processing
相关资源 [Python] 09 - Multi-processing [Pandas] 01 - A guy based on NumPy [AI] 深度数学 - Bayes 这章非常有意思,但一定 ...
- UnicodeDecodeError: 'gbk' codec can't decode byte 0xb0 in position 279: illegal multibyte sequence
with open(r'E:\yy\mysql.txt') as wk: print(wk.readlines()) Traceback (most recent call last): File & ...
- jenkins自动化部署项目5 -- 系统管理-系统设置ssh配置
[系统管理]-[系统设置] 如果应用服务(前端后台)要部署在linux服务器上,我选择的是用ssh 为了jenkins登录远程登录linux服务器可以免密登录,先配置公钥和私钥: 我是在windows ...
- java项目打包
http://blog.csdn.net/qq_34845382/article/details/53885907 自己用Rinnable JAR file 方法也可以.更简单.直接点击Finish即 ...
- java字符串,数组,集合框架重点
1.字符串的字面量是否自动生成一个字符串的变量? String str1 = “abc”; Sring str2 = new String (“abc”); 对于str1:Jvm在遇到双 ...
- 遇见Python
开发: 开发语言: 1.高级语言:Python Java.PHP C# Go ruby C++... ===> 字节码 2.低级语言:C.汇编 ...
- maven私服 nexus 的安装与使用
简介 私服不是Maven的核心概念,它仅仅是一种衍生出来的特殊的Maven仓库.通过建立自己的私服,就可以降低中央仓库负荷.节省外网带宽.加速Maven构建.自己部署构建等,从而高效地使用Maven. ...
- FFmpeg(三) 编解码相关函数理解
一.编解码基本流程 主要流程: 打开视频解码器(音频一样) 软解码.硬解码 进行编解码 下面先来看打开视频解码器 ①avcodec_register_all()//初始化解码 ②先找到解码器. 找解码 ...
- Spring MVC拦截器学习
1 介绍 Spring Web MVC是基于Servlet API构建的原始Web框架. 2 拦截器 2.1 定义 springmvc框架的一种拦截机制 2.2 使用 2.2.1 两步走 实现Hand ...