众所周知,线段树是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的更多相关文章

  1. 『线段树 Segment Tree』

    更新了基础部分 更新了\(lazytag\)标记的讲解 线段树 Segment Tree 今天来讲一下经典的线段树. 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间 ...

  2. 线段树(Segment Tree)(转)

    原文链接:线段树(Segment Tree) 1.概述 线段树,也叫区间树,是一个完全二叉树,它在各个节点保存一条线段(即“子数组”),因而常用于解决数列维护问题,基本能保证每个操作的复杂度为O(lg ...

  3. 【转】Senior Data Structure · 浅谈线段树(Segment Tree)

    本文章转自洛谷 原作者: _皎月半洒花 一.简介线段树 ps: _此处以询问区间和为例.实际上线段树可以处理很多符合结合律的操作.(比如说加法,a[1]+a[2]+a[3]+a[4]=(a[1]+a[ ...

  4. BZOJ.4695.最假女选手(线段树 Segment tree Beats!)

    题目链接 区间取\(\max,\ \min\)并维护区间和是普通线段树无法处理的. 对于操作二,维护区间最小值\(mn\).最小值个数\(t\).严格次小值\(se\). 当\(mn\geq x\)时 ...

  5. 【数据结构系列】线段树(Segment Tree)

    一.线段树的定义 线段树,又名区间树,是一种二叉搜索树. 那么问题来了,啥是二叉搜索树呢? 对于一棵二叉树,若满足: ①它的左子树不空,则左子树上所有结点的值均小于它的根结点的值 ②若它的右子树不空, ...

  6. 线段树(segment tree)

    线段树在一些acm题目中经常见到,这种数据结构主要应用在计算几何和地理信息系统中.下图就为一个线段树: (PS:可能你见过线段树的不同表示方式,但是都大同小异,根据自己的需要来建就行.) 1.线段树基 ...

  7. 浅谈线段树 (例题:[USACO08FEB]酒店Hotel)By cellur925

    今天我们说说线段树. 我个人还是非常欣赏这种数据结构的.(逃)因为它足够优美,有递归结构,有左子树和右子树,还有二分的思想. emm这个文章打算自用,就不写那些基本的操作了... 1° 简单的懒标记( ...

  8. 线段树 Interval Tree

    一.线段树 线段树既是线段也是树,并且是一棵二叉树,每个结点是一条线段,每条线段的左右儿子线段分别是该线段的左半和右半区间,递归定义之后就是一棵线段树. 例题:给定N条线段,{[2, 5], [4, ...

  9. (转)浅谈trie树

    浅谈Trie树(字典树)         Trie树(字典树) 一.引入 字典是干啥的?查找字的. 字典树自然也是起查找作用的.查找的是啥?单词. 看以下几个题: 1.给出n个单词和m个询问,每次询问 ...

随机推荐

  1. MOOC C++笔记(二):类和对象基础

    第二周:类和对象基础 面向对象程序设计的四个基本特点 抽象.封装.继承.多态. 面向对象程序设计的过程 1.从客观事物抽象出类 抽象出的事物带有成员函数与成员变量(类似于带函数的结构体) 成员变量和成 ...

  2. BOM之定时器

    JavaScript中的时间是通过定时器控制的,他们分别是window.setInterval和window.setTimeout,我们当然可以省略window,直接使用方法名称调用. 一     s ...

  3. 规则引擎 - drools 使用讲解(简单版) - Java

    drools规则引擎 项目链接 现状: 运维同学(各种同学)通过后台管理界面直接配置相关规则,这里是通过输入框.下拉框等完成输入的,非常简单: 规则配置完毕后,前端请求后端,此时服务端根据参数(即规则 ...

  4. SpringCloud实现服务间调用(RestTemplate方式)

    上一篇文章<SpringCloud搭建注册中心与服务注册>介绍了注册中心的搭建和服务的注册,本文将介绍下服务消费者调用服务提供者的过程. 本文目录 一.服务调用流程二.服务提供者三.服务消 ...

  5. Two progressions CodeForce 125D 思维题

    An arithmetic progression is such a non-empty sequence of numbers where the difference between any t ...

  6. Python基础(十四)

    今日主要内容 装饰器扩展 有参装饰器 多个装饰器装饰一个函数 递归 一.装饰器扩展 (一)含有参数的装饰器 先来回顾一下装饰器的标准模式 def wrapper(fn): def inner(*arg ...

  7. 使用tomcat7发布war项目启动org_apache_tomcat_websocket报错

    在使用tomcat7发布项目时(项目是用springboot 2.1.4.RELEASE版本开发的) 换成tomcat8就可以正常发布了,网上说tomcat7不支持servlet3.1,升级到spri ...

  8. 【ADO.NET基础-Login】带验证码验证的登录界面(用于简单的基础学习)

    以下代码如果有不对或者不妥之处,还望大神们指点一二 或者有同学者有问题或建议,一定要提出来,共同探讨 小弟在此感谢! 前台代码: <!DOCTYPE html> <html xmln ...

  9. THINKPHP 中关联查询(多表查询)

    THINKPHP 中关联查询(多表查询)可以使用 table() 方法或和join方法,请看示例: 1.Table方法:定义要操作的数据表名称,可以动态改变当前操作的数据表名称,需要写数据表的全名,包 ...

  10. .Net Core 商城微服务项目系列(五):使用Polly处理服务错误

    项目进行微服务化之后,随之而来的问题就是服务调用过程中发生错误.超时等问题的时候我们该怎么处理,比如因为网络的瞬时问题导致服务超时,这在我本人所在公司的项目里是很常见的问题,当发生请求超时问题的时候, ...