「学习笔记」线段树标记永久化

这篇具有很好参考价值的文章主要介绍了「学习笔记」线段树标记永久化。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

第一次见到这个词是在 zkw 线段树的课件里见到的。

标记永久化可以避免下传懒惰标记,只需在进行询问时把标记的影响加到答案当中,从而降低程序常数。

「学习笔记」线段树标记永久化
洛谷的模板题也证明,确实是小常数。
这三次提交都是递归写法,如果搭配 zkw 线段树,应该会跑得更快。

具体操作

我们在讲懒标向下递归的过程中,如果当前区间正好等于查询区间,那就直接改懒标和数值,倘若当前区间包含查询区间但不与查询区间相等,那我们只修改值,这些操作与线段树修改操作很像。

inline void modify(int u, int l, int r, int lr, int rr, ll c) {
	t[u].val += (rr - lr + 1) * c;
	if (lr == l && r == rr) {
		t[u].laz += c;
		return ;
	}
	if (rr <= mid)	modify(ls, l, mid, lr, rr, c);
	else if (lr > mid)	modify(rs, mid + 1, r, lr, rr, c);
	else {
		modify(ls, l, mid, lr, mid, c);
		modify(rs, mid + 1, r, mid + 1, rr, c);
	}
}

需要注意的是,如果查询的区间横跨左右两个孩子区间,那我们需要将查询区间也从 mid 处分开。


设置好懒标,查询时该如何处理懒标呢?
按照一般的写法,在向下递归时,我们还要用递归把懒标也一起向下传递,而标记永久化则是舍弃了向下传递懒标这个操作,我们在查询时设置一个值,用它来记录沿路的懒标,最后一起统计即可。
为什么要记录沿路的懒标呢?
如果包含该区间的大区间被打上了懒标,则说明这一整个大区间都受到这个懒标的影响,所以把它记录下来。

inline ll query(int u, int l, int r, int lr, int rr, ll add) {
	if (lr == l && r == rr) {
		return t[u].val + add * t[u].len;
	}
	ll sum = 0;
	if (rr <= mid) {
		sum = query(ls, l, mid, lr, rr, add + t[u].laz);
	}
	else if (lr > mid) {
		sum = query(rs, mid + 1, r, lr, rr, add + t[u].laz);
	}
	else {
		sum = query(ls, l, mid, lr, mid, add + t[u].laz) 
		+ query(rs, mid + 1, r, mid + 1, rr, add + t[u].laz);
	}
	return sum;
}

最后处理答案时,就是将懒标的和乘上这个区间的长度,add 记录的是懒标和,可以将这个 add 看作是对于这个区间的每个元素一共要增加的值。

总结

好处:

  1. 码量小,不用写 pushdownpushup
  2. 在可持久化线段树上应用该技巧能做到区间修改的效果。

坏处:

  1. 适用范围有限,只有当求的东西满足区间贡献独立。比如区间加法。
    区间最大值就无法标记永久化。
  2. 多标记好像也不适用。

总归来说,对于一般的线段树,递归写法就足够了,标记永久化用的较少,对于线段树套线段树这样的应该会用的比较多。

例题

【模板】线段树 1文章来源地址https://www.toymoban.com/news/detail-448256.html

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (u << 1)
#define rs (u << 1 | 1)
#define mid ((l + r) >> 1)

const int N = 1e5 + 5;

int n, m;

struct seg_tree {
	int len;
	ll val, laz;
} t[N << 2];

inline void build(int u, int l, int r) {
	t[u].len = r - l + 1, t[u].laz = 0;
	if (l == r) {
		cin >> t[u].val;
		return ;
	}
	build(ls, l, mid);
	build(rs, mid + 1, r);
	t[u].val = t[ls].val + t[rs].val;
}

inline void modify(int u, int l, int r, int lr, int rr, ll c) {
	t[u].val += (rr - lr + 1) * c;
	if (lr == l && r == rr) {
		t[u].laz += c;
		return ;
	}
	if (rr <= mid)	modify(ls, l, mid, lr, rr, c);
	else if (lr > mid)	modify(rs, mid + 1, r, lr, rr, c);
	else {
		modify(ls, l, mid, lr, mid, c);
		modify(rs, mid + 1, r, mid + 1, rr, c);
	}
}

inline ll query(int u, int l, int r, int lr, int rr, ll add) {
	if (lr == l && r == rr) {
		return t[u].val + add * t[u].len;
	}
	ll sum = 0;
	if (rr <= mid) {
		sum = query(ls, l, mid, lr, rr, add + t[u].laz);
	}
	else if (lr > mid) {
		sum = query(rs, mid + 1, r, lr, rr, add + t[u].laz);
	}
	else {
		sum = query(ls, l, mid, lr, mid, add + t[u].laz) 
		+ query(rs, mid + 1, r, mid + 1, rr, add + t[u].laz);
	}
	return sum;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	cin >> n >> m;
	build(1, 1, n);
	for (int i = 1, op, x, y; i <= m; ++ i) {
		cin >> op >> x >> y;
		if (op == 1) {
			ll k;
			cin >> k;
			modify(1, 1, n, x, y, k);
		}
		else {
			cout << query(1, 1, n, x, y, 0) << '\n';
		}
	}
	return 0;
}

到了这里,关于「学习笔记」线段树标记永久化的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [HDU - 4578]Transformation(线段树+多重懒标记)

    这道题涉及到了区间操作,所以我们用线段树算法。同时,这道题里面有区间修改的操作,所以我们还要用到懒标记。 这里一共有三种区间的操作,分别是:加、乘、赋值。这三种操作无法用一个懒标记来统一,所以我们需要使用三个懒标记来完成这道题。 这道题的查询操

    2023年04月25日
    浏览(16)
  • 「学习笔记」线段树

    线段树是一棵二叉搜索树,思想与分治很想,把一段区间平分平分再平分,平分到不能平分为止,可以进行方便的区间修改和区间查询,当然,树状数组能做的单点修改、单点查询,线段树也可以更好地实现,总之,线段树是树状数组的升级版,此外,线段树能做的平衡树也

    2024年02月07日
    浏览(13)
  • 可持久化线段树15(思维+乱搞)

    P4559 [JSOI2018]列队 首先思考: 对于 [ l , r ] [l,r] [ l , r ] 区间内的同学,他们集合之后与集合之前的相对大小是不会改变的,所有无论是否集合他们的相对的顺序是不变的,那么集合到 [ k , k + r − l ] [k,k+r-l] [ k , k + r − l ] 的时候,他们的位置一定是固定的,我们可以将其称之

    2024年02月09日
    浏览(21)
  • UE5学习笔记(十四)——蓝图基础之第一次做界面

    目录 制作一个简单的UI 步骤1:添加一个界面,并显示在屏幕上 【知识点】在关卡界面调用控件的值 步骤2:蓝图控制文字改变

    2024年02月04日
    浏览(36)
  • Unity学习笔记--数据持久化Json

    json是国际通用语言,可以跨平台(游戏,软件,网页,不同OS)使用, json语法较为简单,使用更广泛。json使用键值对来存储。 认识json文件 //注意字典类型存储时,键是以string类型存储的 需要添加 “” Excel转换为JSON文件: 使用网站来转换:bejson 挖坑-----》开发一个工具,

    2024年02月05日
    浏览(36)
  • Unity学习笔记--数据持久化XML文件(1)

    Xml是可拓展标记语言,一种文件格式。我们使用xml来完成对数据持久化的存储。等待我们有一程序运行结束之后,将内存中的数据进行保存,(保存在硬盘/服务器)实现对数据的持久化存储。 xml文件的读取和保存以及修改 要点: XMl文件的加载 XML文件节点的查找访问 XML文件

    2024年02月05日
    浏览(31)
  • Unity学习笔记--数据持久化之PlayerPrefs的使用

    PlayerPrefs是Unity游戏引擎中的一个类,用于在游戏中存储和访问玩家的偏好设置和数据。它可以用来保存玩家的游戏进度、设置选项、最高分数等信息。PlayerPrefs将数据存储在本地文件中,因此可以在游戏重新启动时保持数据的持久性。 PlayerPrefs中存储的数据存储在哪里? PC端

    2024年02月05日
    浏览(33)
  • html5学习笔记16-MathML 数学标记语言,书写数学符号和公式的置标语言

    https://www.runoob.com/html/html5-mathml.html

    2024年02月11日
    浏览(35)
  • RabbitMQ学习笔记(消息发布确认,死信队列,集群,交换机,持久化,生产者、消费者)

    MQ(message queue):本质上是个队列,遵循FIFO原则,队列中存放的是message,是一种跨进程的通信机制,用于上下游传递消息。MQ提供“逻辑解耦+物理解耦”的消息通信服务。使用了MQ之后消息发送上游只需要依赖MQ,不需要依赖其它服务。 功能1:流量消峰 功能2:应用解耦 功

    2024年02月07日
    浏览(31)
  • 小米笔记本15.6pro太难拆啦,记录第一次自己安装固态硬盘

    2018年购入的小米电脑pro15.6英寸 型号:171501-AQ便携式计算机 固态硬盘接口M.2,SATA协议 运行内存8Gb,不能拓展 我从京东商场找到符合此接口和协议的1Tb固态硬盘很少,最后我选择购买西部数据(WD)1TB笔记本电脑SSD固态硬盘SA510 SATA M.2接口Blue系列。(致钛的也不错,价格便宜,

    2024年02月09日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包