zkw线段树学习笔记

今天模拟赛线段树被卡常了,由于我自带常数 \(buff\),所以学了下zkw线段树。

平常的线段树无论是修改还是查询,都是从根开始递归找到区间的,而zkw线段树直接从叶子结点开始操作。

建树

首先,我们需要把线段树补成一个堆形态的树,原序列在最后一层(最后一层的左右要留空,后面再讲为什么),这样一来,就可以轻松得出:原序列里第 \(x\) 个元素在线段树里的编号就是 \(x+2^k\) (其中 \(k\) 为线段树的深度,根节点深度为 \(0\) )

大概就是这样 :



不难发现最后一层节点掐头去尾,就是原序列编号加上二进制最高位上的 \(1\)。

代码实现也非常简单:

segment_tree(const int n = 0) {
for (M = 1; M - 2 < n; M <<= 1); //M是最后一层的节点个数
for (int i = 1; i <= n; ++i)
t[i + M] = in();
for (int i = M - 1; i; --i)
t[i] = t[i << 1] + t[i << 1 | 1]; //合并子树信息
}

单点查询

直接查

单点修改

。。直接上代码:

void update(int p) {
for (p += M; p; p >>= 1) t[p] += k;
}

区间操作

若当前操作区间为 \([x,y]\),可以把它先转为开区间 \((x-1,y+1)\) (最后一层左右要留空的原因),设此开区间左右端点在线段树的编号为 \(l,r\)。

\(l, r\) 同时向它们的父亲节点跳,若 \(l\) 是它父亲的左儿子,则它父亲的右儿子被操作( \(r\) 与 \(l\) 对称),直到 \(l,r\) 的父亲相同。

可以配合图片理解:



上图中:蓝色为 \(l,r\) 会跳到的点。因为查询区间为 \(l\) 右边和 \(r\) 左边组成的区间,当 \(l\) 为父亲的左儿子时,它父亲的右儿子一定在它左边( \(r\) 与 \(l\) 对称)。

区间修改、查询

这里以区间加法为例;

在普通的线段树中,一般用 \(lazy\) \(tag\) 来解决这个问题,zkw线段树同样可以,但从上向下操作、下放 \(lazy\) \(tag\) 等操作并不优雅 常数大

可以采用标记永久化,随用随查,按zkw的说法——永久化的标记就是值。

考虑上文提到的区间操作的过程,自下向上走的过程中,根据遇到的标记来计算贡献。

具体实现细节可以看代码:

void modify(int l, int r, ll k) {
int lnum = 0, rnum = 0, now = 1;
//lnum 表示当前左端点走到的子树有多少个元素在修改区间内 (rnum与lnum对称)
//now 表示当前端点走到的这一层有多少个叶子节点
for (l = l + M - 1, r = r + M + 1; l ^ r ^ 1; l >>= 1, r >>= 1; now <<= 1) {
t[l] += k * lnum, t[r] += k * rnum;
if (~l & 1) t[l ^ 1] += k * now, add[l ^ 1] += k, lnum += now;
if (r & 1) t[r ^ 1] += k * now, add[r ^ 1] += k, rnum += now;
}
for (; l; l >>= 1, r >>= 1)
t[l] += k * lnum, t[r] += k * rnum;
}
long long query(int l, int r) {
int lnum = 0, rnum = 0, now = 1;
long long ret = 0;
for (l = l + M - 1, r = r + M + 1; l ^ r ^ 1; l >>= 1, r >>= 1, now <<= 1) {
if (add[l]) ret += add[l] * lnum;
if (add[r]) ret += add[r] * rnum;
if (~l & 1) ret += t[l ^ 1], lnum += now;
if (r & 1) ret += t[r ^ 1], rnum += now;
}
for (; l; l >>= 1, r >>= 1)
ret += add[l] * lnum, ret += add[r] * rnum;
return ret;
}

参考文献:《统计的力量》—— zkw

zkw线段树学习笔记的更多相关文章

  1. 线段树学习笔记(基础&进阶)(一) | P3372 【模板】线段树 1 题解

    什么是线段树 线段树是一棵二叉树,每个结点存储需维护的信息,一般用于处理区间最值.区间和等问题. 线段树的用处 对编号连续的一些点进行修改或者统计操作,修改和统计的复杂度都是 O(log n). 基础 ...

  2. JSOI2008 Blue Mary开公司 | 李超线段树学习笔记

    题目链接:戳我 这相当于是一个李超线段树的模板qwqwq,题解就不多说了. 代码如下: #include<iostream> #include<cstdio> #include ...

  3. 仙人掌&圆方树学习笔记

    仙人掌&圆方树学习笔记 1.仙人掌 圆方树用来干啥? --处理仙人掌的问题. 仙人掌是啥? (图片来自于\(BZOJ1023\)) --也就是任意一条边只会出现在一个环里面. 当然,如果你的图 ...

  4. 普及向 ZKW线段树!

    啊,是否疲倦了现在的线段树 太弱,还递归! 那我们就欢乐的学习另外一种神奇的线段树吧!(雾 他叫做zkw线段树   这个数据结构灰常好写(虽然线段树本身也特别好写……) 速度快(貌似只在单点更新方面比 ...

  5. 线段树和zkw线段树

    作者作为一个蒟蒻,也是最近才自学了线段树,不对的地方欢迎大佬们评论,但是不要喷谢谢 好啦,我们就开始说说线段树吧 线段树是个支持区间操作和查询的东东,平时的话还是蛮实用的 下面以最基本的区间加以及查询 ...

  6. ZKW线段树 非递归版本的线段树

    学习和参考 下面是支持区间修改和区间查询的zkw线段树模板,先记下来. #include <algorithm> #include <iterator> #include &l ...

  7. V-Parenthesis 前缀+ZKW线段树或RMQ

    Bobo has a balanced parenthesis sequence P=p 1 p 2…p n of length n and q questions. The i-th questio ...

  8. ZKW线段树

    简介 zkw线段树虽然是线段树的另一种写法,但是本质上已经和普通的递归版线段树不一样了,是一种介于树状数组和线段树中间的存在,一些功能上的实现比树状数组多,而且比线段树好写且常数小. 普通线段树采用从 ...

  9. zkw线段树详解

    转载自:http://blog.csdn.net/qq_18455665/article/details/50989113 前言 首先说说出处: 清华大学 张昆玮(zkw) - ppt <统计的 ...

随机推荐

  1. linq中如何在join中指定多个条件

    public ActionResult Edit(int id) { using (DataContext db = new DataContext(ConfigurationManager.Conn ...

  2. UnityEditorWindow自建窗口扩展

    这里主要记录UnityEditorWindow的创建,以及常用的API接口样式 1,创建UnityEditorWindow 在Unity目录中,创建一个名为Editor的文件夹(任何位置),然后创建如 ...

  3. 微信内无法自动跳转外部浏览器打开H5分享链接的解决办法

    很多情况下我们用微信分享转发H5链接的时候,都无法在微信内打开,即使开始能打开,过一段时间就会被拦截,拦截后再打开微信会提示 “已停止访问该网址” ,那么导致这个情况的因素有哪些呢,主要有以下四点 1 ...

  4. 【问题解决方案】Github中的jupyter notebook文件(.ipynb)加载失败/失败

    两个方法: 法一:本机安装jupyter notebook的情况下直接下载文件并打开 本机打开的话会在浏览器中显示,地址为localhost:8888,也就是本机 法二:在线打开:利用 'https: ...

  5. 【学习总结】GirlsInAI ML-diary day-19-面向对象编程

    [学习总结]GirlsInAI ML-diary 总 原博github链接-day19 认识面向对象 Python使用类(class)和对象(object),进行面向对象(object-oriente ...

  6. git常用命令值stash

    git stash(git储藏)可用于以下情形: 发现有一个类是多余的,想删掉它又担心以后需要查看它的代码,想保存它但又不想增加一个脏的提交.这时就可以考虑git stash. 使用git的时候,我们 ...

  7. P5057 [CQOI2006]简单题

    题目描述 有一个 n 个元素的数组,每个元素初始均为 0.有 m 条指令,要么让其中一段连续序列数字反转——0 变 1,1 变 0(操作 1),要么询问某个元素的值(操作 2). 例如当 n = 20 ...

  8. Adding appsettings.json to a .NET Core console app

    This is something that strangely doesn’t seem to be that well documented and took me a while to figu ...

  9. MVC EF 移除建表时自动加上s的复数形式

    移除建表时自动加上s的复数形式 public class DBContext : DbContext { public DBContext() : base("name=DBContext& ...

  10. Forethought Future Cup - Elimination Round

    A:签到. #include<bits/stdc++.h> using namespace std; #define ll long long char getc(){char c=get ...