首页 > 其他分享 >可持久化线段树学习笔记

可持久化线段树学习笔记

时间:2024-01-15 21:22:34浏览次数:29  
标签:md 持久 lc int 线段 笔记 tot rc root

Q&A

  • 主席树可持久化线段树有什么区别?

    主席树全称:可持久化权值线段树。

定义

可查询与修改历史版本的线段树。

基本思想

根据某个定理:

空间复杂度一定不会超过时间复杂度。

所以我们没有必要在每一次操作时把整个线段树复制一遍。

我们在更新版本时,把我们要访问的节点单独复制一遍,然后重新设置父子关系,最后正常更新数据。

简称:一复二设三更新

实现

struct SegTree {
    int lc[MAXN * 20], rc[MAXN * 20], sum[MAXN * 20], rt[MAXN], tot;

    int nw() {
        tot++;
        lc[tot] = rc[tot] = sum[tot] = 0;
        return tot;
    }

    int build(int l, int r) {
        int root = nw();
        if (l == r)
            return root;
        int md = l + r >> 1;
        lc[root] = build(l, md);
        rc[root] = build(md + 1, r);
        return root;
    }

    int update(int k, int l, int r, int x) {
        // 一复
        int root = nw();
        lc[root] = lc[k];
        rc[root] = rc[k];
        sum[root] = sum[k] + 1;
        // ======
        if (l == r)
            return root;
        int md = l + r >> 1;
        // 二设
        if (x <= md)
            lc[root] = update(lc[root], l, md, x);
        else
            rc[root] = update(rc[root], md + 1, r, x);
        // 三更新
        sum[root] = sum[lc[root]] + sum[rc[root]];
        return root;
    }

    int query(int k, int l, int r, int x) {
        if (l == r)
            return l;
        int t = sum[lc[k]];
        int md = l + r >> 1;
        if (x <= t)
            return query(lc[k], l, md, x);
        else
            return query(rc[k], md + 1, r, x - t);
    }
} seg;

以上是单点修改区间查询。

根据这个口诀就能很快写出来。

标记永久化

我们如果要进行区间修改区间查询,这个时候就会用到懒标记。

但是这里有个问题,我们不能对原树进行修改,如果修改了就会影响到历史值查询。

但是 pushdown 会神不知鬼不觉地修改历史数据,如果你每一次 pushdown 都新建节点,那空间可能要去见阎王老爷了。

这个时候标记永久化是个很好的选择。

什么意思?就是把 pushdown 直接删除,然后直接在答案统计的时候算上懒标记的贡献就行了。

struct SegTree {
    int lc[MAXN * 40], rc[MAXN * 40], l[MAXN * 40], r[MAXN * 40], sum[MAXN * 40], rt[MAXN], lz[MAXN * 40], tot;

    int nw() {
        tot++;
        lc[tot] = rc[tot] = sum[tot] = 0;
        l[tot] = r[tot] = 0;
        lz[tot] = 0;
        return tot;
    }

    void pushup(int k, int L, int R) {
        sum[k] = sum[lc[k]] + sum[rc[k]] + lz[k] * (R - L + 1);
        // if (k == 24)
        //     cerr << sum[k] << endl;
    }

    int build(int L, int R) {
        int root = nw();
        l[root] = L;
        r[root] = R;
        if (L == R) {
            sum[root] = v[L];
            return root;
        }
        int md = L + R >> 1;
        lc[root] = build(L, md);
        rc[root] = build(md + 1, R);
        pushup(root, L, R);
        return root;
    }

    int update(int k, int L, int R, int x) {
        int root = nw();
        lc[root] = lc[k];
        rc[root] = rc[k];
        l[root] = l[k];
        r[root] = r[k];
        lz[root] = lz[k];
        sum[root] = sum[k];
        if (l[root] == L and r[root] == R) {
            sum[root] += x * (R - L + 1);
            lz[root] += x;
            return root;
        }
        int md = (l[root] + r[root]) / 2;
        if (R <= md)
            lc[root] = update(lc[root], L, R, x);
        else if (L > md)
            rc[root] = update(rc[root], L, R, x);
        else {
            lc[root] = update(lc[root], L, md, x);
            rc[root] = update(rc[root], md + 1, R, x);
        }
        pushup(root, l[root], r[root]);
        return root;
    }

    int query(int k, int L, int R) {
        if (l[k] == L and r[k] == R)
            return sum[k];
        int md = (l[k] + r[k]) / 2;
        int ans = lz[k] * (R - L + 1);
        if (R <= md)
            ans += query(lc[k], L, R);
        else if (L > md)
            ans += query(rc[k], L, R);
        else {
            ans += query(lc[k], L, md);
            ans += query(rc[k], md + 1, R);
        }
        return ans;
    }

    void print(int k) {
        if (!k)
            return;
        cerr << k << "\t" << l[k] << "\t" << r[k] << "\t" << lc[k] << "\t" << rc[k] << "\t" << sum[k] << "\t" << lz[k] << endl;
        print(lc[k]);
        print(rc[k]);
    }
} seg;

这就是标记永久化实现的区间修改区间查询。

具体运用

实际上运用的更多的是主席树(本质上差不多),就是离散化+值域维护。

区间第 \(k\) 小/大

将序列的 \(n\) 个元素依次插入主席树中,查询区间第 \(k\) 小就是查询时看左子树在这个历史内的数的数量是否大于等于排名,大于等于就向左子树扫,不然向右子树扫。

细心的人可能发现,实际上主席树的运用范围覆盖了普通莫队乃至带修莫队,而且时间复杂度也更胜一筹,但是较莫队来讲思想较复杂,调试较困难,需要均衡考量。

各种时间穿梭

这种就不用说了,很裸。

标签:md,持久,lc,int,线段,笔记,tot,rc,root
From: https://www.cnblogs.com/LightningCreeper/p/17966368

相关文章

  • 数学建模入门笔记(1)——Python pulp库解线性规划问题
    参考:Python求解线性规划——PuLP使用教程-Only(AR)-博客园(cnblogs.com)1.Definethemodelmodel=pl.LpProblem(name="",sense=pl.LpMaximize)name模型的名字sense模型的类型(pl.LpMaximize/pl.LpMinimize)2.Definethedecisionvariables用x[i]存储变量,命名为xi......
  • stm32笔记[11]-驱动SSD1306屏幕
    摘要在蓝桥杯物联网的CT127C开发板上驱动SSD1306的0.91寸显示屏.平台信息KeilMDK-ARM(μVision)V5.35.0.0STM32CubeMX6.2.1原理简介CT127C开发板简介蓝桥物联网竞赛实训装置省赛训练套装,适用于蓝桥杯大赛(电子类)物联网设计与开发科目竞赛训练及高校日常教学实训环......
  • 数据库学习笔记(一)—— 初识MySQL
    初识MySQL介绍什么是数据库? 数据库是结构化信息或数据的有序集合,一般以电子形式存储在计算机系统中。通常由数据库管理系统(DBMS) 来控制。在现实中,数据、DBMS及关联应用一起被称为数据库系统,通常简称为数据库。数据库与电子表格有何区别?数据库和电子表格(例如......
  • openGauss学习笔记-198 openGauss 数据库运维-常见故障定位案例-分析查询效率异常降低
    openGauss学习笔记-198openGauss数据库运维-常见故障定位案例-分析查询效率异常降低的问题198.1分析查询效率异常降低的问题198.1.1问题现象通常在几十毫秒内完成的查询,有时会突然需要几秒的时间完成;而通常需要几秒完成的查询,有时需要半小时才能完成。198.1.2处理办法通......
  • 2024.1.15-每日进度笔记
    今天,我尝试在java中对昨天的python脚本进行调用,并尝试对输出结果进行格式化。 参考:百度文心一言的回复。 packagetest0113;importjava.io.BufferedReader;importjava.io.IOException;importjava.io.InputStreamReader;importjava.util.Arrays;publicclasstes......
  • 【计网笔记】互联网标准化工作
    互联网标准化工作互联网标准化组织:互联网协会1992年由于互联网不再归美国政府管辖,因此成立了一个国际性组织叫作互联网协会(InternetSociety,简称为ISOC)[W-ISOC],以便对互联网进行全面管理以及在世界范围内促进其发展和使用。ISOC下面有一个技术组织叫作互联网体系结构委员会I......
  • asymmetric loss学习笔记
    在看RAM++模型的时候,看到了用的损失函数是asymmetricloss,称为非对称损失。以二分类问题为例,正类别和负类别的损失权重可以不相等。这样设计的目的是使模型更关注于对某一类别的正确分类,尤其是当某一类别的错误分类可能带来更严重后果时。这个损失常常与focalloss一起做对比,"不......
  • c#学习笔记----------------------------协变和逆变
    协变和逆变协变和逆变能够实现数组类型、委托类型和泛型类型参数的隐式引用转换。协变保留分配兼容性,逆变则与之相反。协变以下代码演示支持协变与不支持协变的泛型和数组的区别//泛型委托publicdelegateTMyFuncA<T>();//不支持逆变与协变......
  • ICLR 2022: Anomaly Transformer论文阅读笔记(2) 深度解析代码
    AnomalyTransformer是一个由Transformer:AttentionIsAllYouNeed启发出的检测时间序列异常点的无监督学习算法。在这一篇我会深度解析论文算法以及代码的一一对应,让人更方便能读懂和使用源代码。阅读笔记前篇:ICLR2022:AnomalyTransformer论文阅读笔记+代码复现阅读前提......
  • 《程序员修炼之道:从小工到专家》第七第八章读书笔记
    第七章在项目开始之前第36节异想天开的需求追求完美:完美不是在无所需增加的情况下达到的,而是在没有冗余之时实现的。因此,我们应该避免收集过多需求,而是专注于深入挖掘需求,围绕核心功能不断打磨。与用户共同工作:挖掘需求需要与用户一同工作,以用户的思维方式思考问题。......