首页 > 其他分享 >标记永久化

标记永久化

时间:2024-04-20 21:13:44浏览次数:23  
标签:标记 int sum add 永久化 tag 区间

也就是没有 push_down

注意前面两个问题是具有启发性的,不要略去不看

区间加,单点和

每个节点 \(u\) 额外维护一个 add 值,表示 \(u\) 所代表的区间的总增加量

此时只考虑该区间的增加情况,不用考虑它祖先或者儿子的增加情况

这样做的代价是查询时把没考虑的祖先 以及 儿子 的增加情况考虑进去

区间加:把 \(\log n\) 个点的 add 加一下

单点和:对于一个单点,所有包含它的区间的 add 都对他有贡献

于是把经过的节点的 add 累加起来回答

区间加,区间和

对当前区间 \(u\),可以想到维护两个信息:

  • \(u\) 的区间和 \(sum(u)\)
  • \(u\) 的增加量 \(add(u)\)

但是这两个标记是如何具体地维护从而支持区间加、区间和的呢?我们不知道

注意首先要想求出 \(u\) 的答案,我们需要考虑除了 \(u\) 本身被修改的影响之外的两个影响:

  • \(u\) 的儿子对 \(u\) 答案的影响
  • \(u\) 的祖先对 \(u\) 答案的影响

容易想到让 \(u\) 的标记维护第一个影响,然后在查询的时候对答案考虑第二个影响(查询时路过的节点都是当前查询节点的祖先,而 push_up 就相当于用儿子更新 \(u\))

而由于询问时对于一个祖先需要方便地算出它对它的子树的影响,于是我们令 \(add(u)\) 表示 \(u\) 整个区间所有数都同步累加的增加量

此时 query 的祖先的影响就容易维护了,而 update 对该标记的影响也就是对所操作区间的 \(add\) 累加.

而由于节点内部需要记录所有儿子对 \(u\) 的影响,所以令 \(sum(u)\) 为考虑 \(u\) 子树内所有儿子对 \(u\) 影响的区间和

此时对于 \(sum\) 有两种维护方法:

  • 一种方法:

    考虑通过 push_up 更新儿子对 \(u\) 的影响

    假设此时儿子的信息都已经搞定,也就是儿子的 \(sum\) 已经考虑了儿子及其子树的修改情况,现在我们需要用儿子的信息更新 \(u\) 的信息

    直接 \(sum(u)=sum(lc)+sum(rc)\) 即可

    而对于当前修改节点,修改 \(sum\) 即可

  • 一种方法:

    update 时顺带把路过的节点的 \(sum\) 更新了,因为路过的节点一定是当前修改的节点的祖先,这次 update 一定影响到了这些点的区间和

注意虽然这两种方法的思想不同,一种是在回溯时更新,一种是在递归过程中更新,但是代码差异很小,同时这里建议写 push_up

然后在 query 时直接用 \(sum(u)\) 即可

所以 \(sum\) 这个标记是只考虑 \(u\) 这个单独的区间的内部情况的,也就是只考虑了 \(u\) 以及 其子树所有的修改情况,没有考虑其祖先的修改对它的影响

于是我们就推导出了标记永久化的过程.

总结:

注意这个问题中区间 \(u\) 维护了如下两个信息:

  • 考虑 \(u\) 子树内的所有修改(增加)情况,\(u\) 的区间和 \(sum(u)\)
  • 考虑 \(u\) 作为祖先,它的修改(增加)对儿子的答案的贡献 \(add(u)\)

更形象地,\(sum(u)\) 的范围是 \(u\) 的整棵子树,而 \(add(u)\) 的范围是 \(u\) 这一个节点,对儿子的答案的贡献

这种标记维护方式就是标记永久化的核心思想.

而这个方法的必要性和充分性都在前面的一步步推导中体现了.

模板题代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
constexpr int N = 1e5 + 10;
ll sum[N << 2], add[N << 2];
int n, m;

#define lc (u << 1)
#define rc ((u << 1) | 1)
#define mid ((l + r) >> 1)

void Up(int u, int l, int r) { sum[u] = sum[lc] + sum[rc] + 1ll * (r - l + 1) * add[u]; }
void build(int u, int l, int r) {
    if (l == r) return cin >> sum[u], void();
    build(lc, l, mid), build(rc, mid + 1, r), Up(u, l, r);
}
void Upd(int u, int l, int r, int x, int y, ll v) {
    if (y < l || r < x) return;
    if (x <= l && r <= y) return add[u] += v, sum[u] += 1ll * (r - l + 1) * v, void();
    Upd(lc, l, mid, x, y, v), Upd(rc, mid + 1, r, x, y, v), Up(u, l, r);
}
ll Qry(int u, int l, int r, int x, int y) {
    if (y < l || r < x) return 0ll;
    if (x <= l && r <= y) return sum[u];
    return Qry(lc, l, mid, x, y) + Qry(rc, mid + 1, r, x, y) + 1ll * (min(y, r) - max(x, l) + 1) * add[u];
}

int main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    cin >> n >> m, build(1, 1, n);
    while (m--) {
        int op;
        cin >> op;
        if (op == 2) {
            int l, r;
            cin >> l >> r;
            cout << Qry(1, 1, n, l, r) << "\n";
        } else {
            int l, r; ll c;
            cin >> l >> r >> c;
            Upd(1, 1, n, l, r, c);
        }
    }
}

修改时改一路上的 sum、完全包含的 tag
查询时加一路上的 tag、完全包含的 sum

区间赋值、单点查询

假设前面两节你都看懂了

发现从上往下合并标记的时候有困难:如何合并?

发现只要保留最晚的赋值标记就可以

于是再维护一个时间戳,表示这个赋值标记的时间,合并时保留最晚的标记

区间加、区间赋值、区间和

维护 \(sum,add,tag,time\),\(add\) 和 \(tag\) 任意时刻总是保留其一

区间加:若有 \(tag\),\(tag\gets tag+x,time\gets now\);否则 \(add\gets add+x\)

区间赋值:\(tag\gets x,time\gets now\)

区间和:合并标记时 \(add\) 和 \(tag\) 保留其一

  • 有 \(tag\):若当前节点有 \(tag\) 保留最晚的 \(tag\),否则 \(tag\gets tag+add_u\)
  • 无 \(tag\):若当前节点有 \(tag\) 则 \(tag\gets tag_u+add,add\gets0\),否则 \(add\gets add+add_u\)

算答案时类似

区间整除、区间加、区间和

标记 \((a,b)\) 表示先加 \(a\) 再除以 \(b\),记 \(*\) 为标记合并

区间加:\(\left\lfloor\dfrac{x+a}b\right\rfloor+p=\left\lfloor\dfrac{x+a+bp}b\right\rfloor\)

区间整除:\(\left\lfloor\dfrac{\left\lfloor\frac{x+a}b\right\rfloor}p\right\rfloor=\left\lfloor\dfrac{x+a}{bp}\right\rfloor\)

这就说明了 \((a,b)*(c,d)=(a+bc,bd)\),没有交换律

网上有个说法是若能找到一个 \(a'\) 使得 \(a*b=b*a'\),就可以做

具体是强制让儿子的标记比父亲的标记晚

但是这样不可行,复杂度是错的

于是这道题可以普通线段树做

区间加、区间和、区间和历史最大值

标记 \((a,b,c)\) 表示该区间加了 \(a\),区间和为 \(b\),区间和历史最大值为 \(c\)

区间加:\((a,b,c)\to(a+p,b+(r-l+1)\cdot p,\max(c,b+(r-l+1)\cdot p))\)

这是可以标记永久化的

区间 checkmax、区间 max、单点查

标记 \((a,b)\) 表示 checkmax 了 \(a\),当前区间 \(\max=b\)

区间 checkmax:\((a,b)\to(\max(a,x),\max(b,x))\)

这也可以方便的标记永久化

区间加、区间乘、区间和

标记 \((a,b,c)\) 表示加、乘、和,钦定先乘再加(即 \(x\) 的真实值为 \(ax+b\))

区间加:\((a,b,c)\to(a+x,b,c+x)\)

区间乘:\((a,b,c)\to(ax,bx,cx)\)

所以 \((a,b,c)*(d,e)=(ae+d,be,ce+d)\)

可以普通线段树做


不具有交换律的标记如何维护?

一个通用做法是:对每个标记维护时间戳

从根节点走到当前节点一共经过了 \(\log n\) 个标记

用 set 维护从根节点到当前点的所有标记,是按时间从早到晚排序

到达相应节点后再把 set 上的标记一个个合并

时间复杂度 \(\mathcal O(n\log^2n)\)

因为它就是普通线段树区间查询再乘了个 set 的 log

而且实际上遍历 set 是 \(\mathcal O(n)\) 的,\(n\) 为 set 内元素个数

我原来以为它是 3 个 log 的……

标签:标记,int,sum,add,永久化,tag,区间
From: https://www.cnblogs.com/laijinyi/p/18148162

相关文章

  • 附图标记录入小工具
    附图标记录入小工具这是一个使用ahk2.0脚本写的从csv文件选择零件名与附图标记的小工具。制作这个小工具的本意主要是方便撰写专利申请文件时正确地插入附图标记。这是其github仓库,您可以从该仓库下载程序文件。使用说明运行与退出只需要在进行文字录入时,打开GenTag.exe,......
  • Cyanine5-CRGDFK(Cy5-CRGD多肽或花青素标记cRGDFK多肽)是一种具有特殊功能的荧光标记物
    【试剂名称】英文名称:Cyanine5-CRGDFK中文名称:Cy5-CRGD多肽【试剂描述】Cyanine5-CRGDFK(Cy5-CRGD多肽或花青素标记cRGDFK多肽)是一种具有特殊功能的荧光标记物。它的荧光光谱特征是近红外波段,这使得它具有优秀的组织穿透力和低背景干扰,适用于深层的荧光成像。Cyanine5-CRGD......
  • python给折线图添加标记
    我需要记录飞机作业的开始时间和结束时间#!usr/bin/envpython#-*-coding:utf-8_*-"""@author:JK@file:jisuan.py@time:2024/03/${DAY}@desc:"""importpandasaspdimportmatplotlib.pyplotaspltimportmatplotlib.tickerastickerinput_f......
  • 【Azure Policy】使用Azure Policy来检查Azure资源名称是否满足正确要求(不满足就拒绝
    问题描述使用AzurePolicy来检查Azure资源名称是否满足正确要求,如果不满足就拒绝创建或标记为不合规non-compliance在创建Azure上资源的时候,有如下需求:1)资源的名称必须以一个前缀开头,如prod,test等。2)资源的名称结尾处必须是一个数字,如0,1,2,3,4,5,6,7,8,9。3)如果不合规,则拒绝新......
  • 半监督学习:探索未标记数据的潜力
    摘要本文旨在深入探讨深度学习中的半监督学习方法。半监督学习是一种介于有监督学习和无监督学习之间的方法,其能够有效地利用带标签和未带标签的数据进行训练。本文将详细阐述半监督学习的基本概念、原理、应用以及未来发展方向。一、引言随着大数据时代的到来,数据量的激增......
  • BFS记忆化搜索---标记
    迷宫(洛谷)题目描述给定一个\(N\timesM\)方格的迷宫,迷宫里有\(T\)处障碍,障碍处不可通过。在迷宫中移动有上下左右四种方式,每次只能移动一个方格。数据保证起点上没有障碍。给定起点坐标和终点坐标,每个方格最多经过一次,问有多少种从起点坐标到终点坐标的方案。输入格式第......
  • 学习Markdown ——— 一种用处超广、超好用的轻量级标记语言
    0、Markdown是什么?Markdown是一种轻量级标记语言。它允许人们使用易读易写的纯文本格式编写文档,然后转换成有效的XHTML(或者HTML)文档。这种语言吸收了很多在电子邮件中已有的纯文本标记的特性。由于Markdown的轻量化、易读易写特性,并且对于图片,图表、数学式都有支持,许多网站......
  • [转]IDEA 打开项目时,代码全被标记为红色
    在IntelliJIDEA中,代码被标记为红色通常表示存在编译错误或无法解析的引用等问题。要解决这个问题,请按照以下步骤进行排查和改进:检查项目结构:确保所有必需的源代码文件夹都被正确地包含在项目的模块设置中。确认项目SDK配置正确无误。在File->ProjectStructure(快捷键:Ctrl+......
  • 高德地图api标记点和线段重合点击响应问题
    问题现象:现在地图上放置了line和marker,marker在line的上层显示这时line和marker同时存在,当line和marker有重合部分并点击重合点时,只响应line对应的click事件,而位于下方的line无法响应对应的click事件如图:原因:点击事件被上层的marker阻挡,marker并未注册点击事件解决方案在am......
  • Windows操作系统中的时间戳(Timestamp)是指用于标记事件发生时间的一种时间表示方式。在
    Windows操作系统中的时间戳(Timestamp)是指用于标记事件发生时间的一种时间表示方式。在计算机系统中,时间戳通常用来记录文件的创建时间、修改时间、访问时间等信息,也常用于网络通信中的认证和数据同步等场景。以下是Windows时间戳的基础技术原理:系统时钟:Windows操作系统通过系统......