首页 > 其他分享 >zkw线段树

zkw线段树

时间:2024-08-12 10:16:22浏览次数:9  
标签:maxx minn 线段 tt 区间 zkw 节点

介绍

非递归线段树实现方法,码量较短。

zkw 线段树的构造原理:

普通线段树采用堆存储,zkw线段树 本质上是满二叉树(若没有该区间则为空点)

但根据实际情况,原区间不一定构成满二叉树,据查询方式限制,空间开到最接近的 \(2^n\)(据性质树值域 = 底层节点数),即不存在的点有虚点填充。

既然不存在区间也需用空点填充,zkw线段树 对比 普通线段树 空间较大?

相比于普通线段树的结构混乱,一般开到 4 倍空间,而 zkw线段树 则只大约开到 3 倍空间

普通线段树本质上是从上往下搜索,从根节点向下操作。

zkw 线段树不同于普通线段树,本质上是从下往上搜索,从叶子节点向上操作。

zkw 线段树的基本操作

建树操作:

while (m <= n) {
    m <<= 1;
}
for (int i = m + 1; i <= m + n; ++ i) {
    tr[i] = read ();
}
m -= 1;
while (m --) {
    solve ();
}

维护区间和:\(\tt tr[] \rightarrow sum[]\)

solve () : sum[i] = sum[i << 1] + sum[i << 1 | 1];

维护区间最小值:\(\tt tr[] \rightarrow minn[]\)

solve () : minn[i] = min (minn[i << 1], minn[i << 1 | 1]); // 不支持修改
solve () : minn[i] = min (minn[i << 1], minn[i << 1 | 1]);
minn[i << 1] -= minn[i];
minn[i << 1 | 1] -= minn[i];

维护区间最大值:\(\tt tr[] \rightarrow maxx[]\)

solve () : maxx[i] = max (maxx[i << 1], maxx[i << 1 | 1]); // 不支持修改
solve () : maxx[i] = max (maxx[i << 1], maxx[i << 1 | 1]);
maxx[i << 1] -= maxx[i];
maxx[i << 1 | 1] -= maxx[i];

单点查询:

沿根节点向叶子节点累计加和

ll total = 0;
for (int i = id + m; i; i >>= 1) {
    total += minn[i]; 
}

单点修改:

修改当前节点并更新父亲节点

tr[id = id + m] += v;
while (m) {
    solve ();
    m >>= 1;
}

维护区间和:\(\tt tr[] \rightarrow sum[]\)

solve () : sum[i] = sum[i << 1] + sum[i << 1 | 1];

维护区间最小值:\(\tt tr[] \rightarrow minn[]\)

solve () : minn[i] = min (minn[i << 1], minn[i << 1 | 1]); // 不支持修改
solve () : minn[i] = min (minn[i << 1], minn[i << 1 | 1]);
minn[i << 1] -= minn[i];
minn[i << 1 | 1] -= minn[i];

维护区间最大值:\(\tt tr[] \rightarrow maxx[]\)

solve () : maxx[i] = max (maxx[i << 1], maxx[i << 1 | 1]); // 不支持修改
solve () : maxx[i] = max (maxx[i << 1], maxx[i << 1 | 1]);
maxx[i << 1] -= maxx[i];
maxx[i << 1 | 1] -= maxx[i];

区间查询

图片来源于网络(https://csacademy.com/app/graph_editor/)

假定先需查询区间 \([1,4]\) 那么操作如下:

  1. 闭区间改开区间:\([1,4] \rightarrow (0,5)\) 扩增至 \((M + 0, M + 5) = (8,13)\)
  2. 判断:左端点 \(1000\) 为 左儿子,其兄弟节点必在区间内,累加 \(total += D[1001_B]\);判断:右端点 \(1101\) 为右儿子,其兄弟节点必在区间内,累加 \(total += D[1101_B]\)
  3. 缩小区间:\(\tt l >>= 1\) \(\rightarrow\) \(\tt 1000_B >> 1 = 0100_B,\) \(\tt r >>= 1\) \(\rightarrow\) \(\tt 1101_B >> 1 = 0110_B\)
  4. 判断:左端点 \(0100\) 为 左儿子,其兄弟节点必在区间内,累加 \(total += D[0101_B]\);判断:右端点 \(1101\) 为左儿子,不操作
  5. 缩小区间:\(\tt l >>= 1\) \(\rightarrow\) \(\tt 0100_B >> 1 = 0010_B,\) \(\tt r >>= 1\) \(\rightarrow\) \(\tt 0110_B >> 1 = 0011_B\)
  6. 此时 节点 \(\tt l\) 和 节点 \(\tt r\) 为兄弟节点,停止操作。

伪代码:

for (int l = 开区间左端点, r = 开区间右端点; l, r 不是兄弟节点; 缩小区间) {
    if (l 为左儿子) {
        total += c[l 的兄弟节点];
    }
    if (r 为右儿子) {
        total += c[r 的兄弟节点];
    }
}

维护区间和:

for (int l = l + m - 1, r = r + m - 1, l ^ r ^ 1; l >>= 1, r >>= 1) {
    if (~ l & 1) {
        total += c[l ^ 1];
    }
    if (r & 1) {
        total += c[r ^ 1];
    }
}

维护区间最小值:

ll L = 0, R = 0;
for (int l = l + m - 1, r = r + m - 1; l ^ r ^ 1; l >>= 1, r >>= 1) {
    L += minn[l], R += minn[r];
    if (~ l & 1) {
        L = min (L, minn[l ^ 1]);
    }
    if (r & 1) {
        R = min (R, minn[r ^ 1]);
    }
}
ll res = min (L, R);
while (l) {
    res += minn[l >>= 1];
}

维护区间最大值:

ll L = 0, R = 0;
for (int l = l + m - 1, r = r + m - 1; l ^ r ^ 1; l >>= 1, r >>= 1) {
    L += maxx[l], R += maxx[r];
    if (~ l & 1) {
        L = max (L, maxx[l ^ 1]);
    }
    if (r & 1) {
        R = max (R, maxx[r ^ 1]);
    }
}
ll res = max (L, r);
while (l) {
    res += maxx[l >> 1];
}

区间修改

咕了。

标签:maxx,minn,线段,tt,区间,zkw,节点
From: https://www.cnblogs.com/SCAtlas-lxy23/p/18354420

相关文章

  • 关于区间加区间查线段树的标记永久化
    单点加区间查的线段树,每个线段树区间的值代表所维护序列在这个区间的总和;区间加单点查的线段树,每个线段树区间的值代表对这个区间总体加了多少。区间加区间查的线段树可以通过综合两种思想实现标记的永久化。线段树将每一个修改或查询区间拆分为\(O(\logw)\)个线段树区间,只要......
  • 李超线段树
    P4097【模板】李超线段树/[HEOI2013]Segment-洛谷|计算机科学教育新生态(luogu.com.cn)要求在平面直角坐标系下维护两个操作:在平面上加入一条线段。记第\(i\)条被插入的线段的标号为\(i\)。给定一个数\(k\),询问与直线\(x=k\)相交的线段中,交点纵坐标最大的线......
  • 线段树:线段树的定义和应用
    引言线段树是一种高级数据结构,用于解决区间查询和更新问题。它在许多应用中都有广泛的使用,例如数组区间的求和、最小值/最大值查询、区间的最小公倍数/最大公约数查询等。本文将详细介绍线段树的定义、构建、应用以及代码实现。目录线段树的定义线段树的应用线段树的构建......
  • P3834 【模板】可持久化线段树 2
    原题链接题解总体上来讲,就是二分\(x\)查询插入\(l-1\)时有多少数小于等于\(x\),查询插入\(r\)时有多少数小于等于\(x\)然后减一减,看看是不是小于等于\(k-1\)我认为目前没有比ai讲的更清楚的了,请点击这里code#include<bits/stdc++.h>usingnamespacestd;/*#def......
  • 线段树优化建图
    今天写了道线段树优化建图的板子题,感觉学算法的还是要记录下来,将来方便复习也算是对竞赛生涯的一种回忆http://codeforces.com/problemset/problem/786/B,洛谷评级还是省选,如果是比赛现场想出来确实厉害,但是现在嘛,已经是时代眼泪了归纳下特点:和区间有关的图论问题直接上代码讲解......
  • 刍议线段树 2 (区间修改,区间查询)
    线段树\(2\)接上一讲https://www.cnblogs.com/yingxilin/p/18350988(没看的同学们可以先看这篇)上一讲里我们已经介绍了单点修改,区间查询的线段树了。在这一讲里,我们开始学习支持区间修改,区间查询的线段树。考虑之前的做法,之前的查询区间会被分为\(O(logn)\),从而求解,但因为......
  • 线段树维护区间方差
    线段树维护区间方差方差区间方差还教室解题思路:利用线段树维护\(a_i\)与\(a_i^2\)\((1\leqi\leqn)\)两个数列,然后使用一个\(lazytag\)来记录是否进行了区间加,最后输出方差的时候使用$$s^2=\sum\limits_{i=1}^n(a_i-\overlinea)^2=(\sum\limits_{i=1}......
  • 洛谷 P3870 开关之线段树板子
    洛谷P3870题解传送锚点摸鱼环节[TJOI2009]开关题目描述现有\(n\)盏灯排成一排,从左到右依次编号为:\(1\),\(2\),……,\(n\)。然后依次执行\(m\)项操作。操作分为两种:指定一个区间\([a,b]\),然后改变编号在这个区间内的灯的状态(把开着的灯关上,关着的灯打开);指定一个区间......
  • 8.9 线段树板子+三分补题+三维的bfs
    nowcoder训练区间线段树板子题,我们只需要把区间每一个点设置成1,然后修改的时候直接改点,然后查区间就行线段树维护最大字段和/01串最大连续1的个数模板题。把白色和黑色看成1/0两个数就行了。#include<bits/stdc++.h>usingnamespacestd;usingi64=longlon......
  • 【线段树合并/树上差分】[P4556 [Vani有约会] 雨天的尾巴 /【模板】线段树合并
    【线段树合并/树上差分】P4556[Vani有约会]雨天的尾巴/【模板】线段树合并思路对\(x,y,lca(u,v),fa_{lca(u,v)}\)四个点进行树上差分,然后用线段树合并动态权值线段树。#include<bits/stdc++.h>usingnamespacestd;usingi64=longlong;template<classNode>str......