zkw线段树--非递归线段树
一:单点修改,区间查询
(这是我认为最好的图解,请按我的解释仔细体味,后面本人没准备图,全靠在这上面脑测)建树:
void build(int n){
for(bit = 1; bit < n + 2; bit <<= 1);
for(int i = 1; i <= n; i++) dat[bit + i] = read();//这是个快读,因为一些题中原数组用处不大,看个人习惯吧,因人和题目而异
for(int i = bit - 1; i; i--) dat[i] = dat[lc(i)] + dat[rc(i)];
}
单点修改:
void modify(int x, int v){
for(dat[x += bit] += v, x >>= 1; x; x >>= 1) dat[x] = dat[lc(x)] + dat[rc(x)];//x 是元素在原数组的位置,结合前面的分析bit + x就是在线段树中的位置,后面不在提及。然后不断跳到父节点修改
}
区间查询:
int quiry(int l, int r){//不要吐槽我的命名啦
int ans = 0;
for(l += (bit - 1), r += (bit + 1); l ^ r ^ 1; l >>= 1, r >>= 1){//传入的参数中[l,r]是待查询区间,而我们定位到的(l, r)是一个包含代查询区间的最小开区间(左右即为哨兵),原因?先接着看下去;l ^ r ^ 1,这是判断是否已经完全覆盖到了要查询的区间,即左右哨兵成为了兄弟(兄弟节点异或结果为1,再异或1为0退出)
if(~l & 1) ans += dat[l ^ 1];//如果左边的哨兵是左子树,因为最小包含的缘故,其右子树必然在区间内,累加入答案,不熟悉位运算的可能要理解下
if(r & 1) ans += dat[r ^ 1];//右哨兵同理
}
return ans;
}
#include<iostream>
using namespace std;
int read(){
int f = 1, x = 0;
char ch = getchar();
while(ch < '0' || ch > '9'){
if(ch == '-') f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return f * x;
}
const int N = 5e5 + 3;
int dat[N * 2], bit;
#define lc(x) x << 1
#define rc(x) (x << 1) | 1
void build(int n){
for(bit = 1; bit < n + 2; bit <<= 1);
for(int i = 1; i <= n; i++) dat[bit + i] = read();
for(int i = bit - 1; i; i--) dat[i] = dat[lc(i)] + dat[rc(i)];
}
void modify(int x, int v){
for(dat[x += bit] += v, x >>= 1; x; x >>= 1) dat[x] = dat[lc(x)] + dat[rc(x)];
}
int quiry(int l, int r){
l = bit + l - 1, r = bit + r + 1;
int ans = 0;
while(l ^ r ^ 1){
if(~l & 1) ans += dat[l ^ 1];
if(r & 1) ans += dat[r ^ 1];
l >>= 1;
r >>= 1;
}
return ans;
}
signed main(){
int n = read(), q = read();
build(n);
while(q--){
int op = read(), x = read(), y = read();
if(op == 1) modify(x, y);
else printf("%d\n",quiry(x, y));
}
return 0;
}
区间修改,区间查询(单点查询直接令查询区间是[x,x]就行)
struct seg{
int dat, lzy;
#define dat(x) tr[x].dat
#define lzy(x) tr[x].lzy
}tr[N << 2];
#define lc(x) x << 1
#define rc(x) (x << 1) | 1
先展示下定义
建树变化不大:
void build(int n){
for(bit = 1; bit < n + 2; bit <<= 1);
for(int i = 1; i <= n; i++) dat(bit + i) = read();
for(int i = bit - 1; i; i--) dat(i) = dat(lc(i)) + dat(rc(i));
}
区间修改:
修改自下而上记录下左右哨兵包含的区间内有多少个元素被覆盖即可
void modify(int l, int r, int v){
int llen = 0, rlen = 0, len = 1;//左右哨兵的被覆盖长度以及向上跳跃后节点区间变化
for(l += (bit - 1), r += (bit + 1); l ^ r ^ 1; l >>= 1, r >>= 1, len <<= 1){
if(~l & 1) lzy(l ^ 1) += v, llen += len;//标记永久化
if(r & 1) lzy(r ^ 1) += v, rlen += len;
dat(l >> 1) += v * llen, dat(r >> 1) += v * rlen; //如果l一直是右子树这llen为0,否则它的父节点的值应该加上左子树的区间增量,r同理
}
for(llen += rlen, l >>= 1; l; l >>= 1) dat(l) += v * llen;//当兄弟相遇直接一路传递到根就行
}
区间查询:
和上面差不多,加上lzy的增量即可
int quiry(int l, int r){
int llen = 0, rlen = 0, len = 1, ans = 0;
for(l += (bit - 1), r += (bit + 1); l ^ r ^ 1; l >>= 1, r >>= 1, len <<= 1){
if(~l & 1) ans += dat(l ^ 1) + lzy(l ^ 1) * len, llen += len;
if(r & 1) ans += dat(r ^ 1) + lzy(r ^ 1) * len, rlen += len;
ans += lzy(l >> 1) * llen + lzy(r >> 1) * rlen;
//if判断为真值的必然是全部包含在待查询区间的,因此直接乘len即可,而当llen和rlen不为0且哨兵的父节点带有懒标记时,答案应加上哨兵的兄弟被包含在区间里的长度
}
for(llen += rlen, l >>= 1; l; l >>= 1) ans += lzy(l) * llen;//因为标记是modify从哨兵一路上传,l,r之间的区间不会有标记,但是含lzy的区间一定是全部有增量,所以要一路上溯累加
return ans;
}
完整代码在这里:
#include<iostream>
using namespace std;
#define int long long
int read(){
int f = 1, x = 0;
char ch = getchar();
while(ch < '0' || ch > '9'){
if(ch == '-') f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return f * x;
}
const int N = 1e6 + 3;
struct seg{
int dat, lzy;
#define dat(x) tr[x].dat
#define lzy(x) tr[x].lzy
}tr[N << 2];
int bit;
#define lc(x) x << 1
#define rc(x) (x << 1) | 1
void build(int n){
for(bit = 1; bit < n + 2; bit <<= 1);
for(int i = 1; i <= n; i++) dat(bit + i) = read();
for(int i = bit - 1; i; i--) dat(i) = dat(lc(i)) + dat(rc(i));
}
void modify(int l, int r, int v){
int llen = 0, rlen = 0, len = 1;
for(l += (bit - 1), r += (bit + 1); l ^ r ^ 1; l >>= 1, r >>= 1, len <<= 1){
if(~l & 1) lzy(l ^ 1) += v, llen += len;
if(r & 1) lzy(r ^ 1) += v, rlen += len;
dat(l >> 1) += v * llen, dat(r >> 1) += v * rlen;
}
for(llen += rlen,l >>= 1; l; l >>= 1) dat(l) += v * llen;
}
int quiry(int l, int r){
int llen = 0, rlen = 0, len = 1, ans = 0;
for(l += (bit - 1), r += (bit + 1); l ^ r ^ 1; l >>= 1, r >>= 1, len <<= 1){
if(~l & 1) ans += dat(l ^ 1) + lzy(l ^ 1) * len, llen += len;
if(r & 1) ans += dat(r ^ 1) + lzy(r ^ 1) * len, rlen += len;
ans += lzy(l >> 1) * llen + lzy(r >> 1) * rlen;
}
for(llen += rlen, l >>= 1; l; l >>= 1) ans += lzy(l) * llen;
return ans;
}
signed main(){
int n = read(), q = read();
build(n);
while(q--){
int op = read();
if(op == 1){
int l = read(), r = read(), k = read();
modify(l, r, k);
}
else{
int x = read();
printf("%lld\n",quiry(x, x));
}
}
return 0;
}
根据某校OJ来看非递归区间加大概比递归快了3倍
总体而言,现在线段树在时间上优化有zkw线段树,空间上有动态开点
参考链接:(如有介意,联系我删除)
主要模板块学习
zkw线段树--非递归线段树的更多相关文章
- 菜鸟笔记:node.js+mysql中将JSON数据构建为树(递归制作树状菜单数据接口)
初学Web端开发,今天是第一次将所学做随笔记录,肯定存在多处欠妥,望大家海涵:若有不足,望大家批评指正. 进实验室后分配到的第一个项目,需要制作一个不确定层级树形菜单的数据接口,对于从来没实战编过程的 ...
- ZKW线段树 非递归版本的线段树
学习和参考 下面是支持区间修改和区间查询的zkw线段树模板,先记下来. #include <algorithm> #include <iterator> #include &l ...
- [模板]非递归线段树(zkw的变异版本)
类似于zkw,但空间只用两倍,zkw要4倍. 链接 可以下传标记,打熟后很好码. #include <set> #include <cmath> #include <cs ...
- hdu 3887 Counting Offspring(DFS序【非递归】+树状数组)
题意: N个点形成一棵树.给出根结点P还有树结构的信息. 输出每个点的F[i].F[i]:以i为根的所有子结点中编号比i小的数的个数. 0<n<=10^5 思路: 方法一:直接DFS,进入 ...
- vue.js 树菜单 递归组件树来实现
树形视图 Example: https://vuefe.cn/v2/examples/tree-view.html 参照前辈方法实现的,觉得不错,记录一下: 父组件: <!-- 菜单树 --&g ...
- BZOJ 3744 Gty的妹子序列 (分块+树状数组+主席树)
题面传送门 题目大意:给你一个序列,多次询问,每次取出一段连续的子序列$[l,r]$,询问这段子序列的逆序对个数,强制在线 很熟悉的分块套路啊,和很多可持久化01Trie的题目类似,用分块预处理出贡献 ...
- 线段树(单标记+离散化+扫描线+双标记)+zkw线段树+权值线段树+主席树及一些例题
“队列进出图上的方向 线段树区间修改求出总量 可持久留下的迹象 我们 俯身欣赏” ----<膜你抄> 线段树很早就会写了,但一直没有总结,所以偶尔重写又会懵逼,所以还是要总结一下. ...
- [bzoj3196][Tyvj1730]二逼平衡树_树套树_位置线段树套非旋转Treap/树状数组套主席树/权值线段树套位置线段树
二逼平衡树 bzoj-3196 Tyvj-1730 题目大意:请写出一个维护序列的数据结构支持:查询给定权值排名:查询区间k小值:单点修改:查询区间内定值前驱:查询区间内定值后继. 注释:$1\le ...
- hdu5044 Tree 树链拆分,点细分,刚,非递归版本
hdu5044 Tree 树链拆分.点细分.刚,非递归版本 //#pragma warning (disable: 4786) //#pragma comment (linker, "/ST ...
- 线段树&&线段树的创建线段树的查询&&单节点更新&&区间更新
目录 线段树 什么是线段树? 线段树的创建 线段树的查询 单节点更新 区间更新 未完待续 线段树 实现问题:常用于求数组区间最小值 时间复杂度:(1).建树复杂度:nlogn.(2).线段树算法复杂度 ...
随机推荐
- Lynx-字节跳动跨平台框架多端兼容Android, iOS, Web 原生渲染
介绍 字节跳动近期开源的跨平台框架Lynx被视为一项重要的技术创新.相较于市场上已有的解决方案如React Native (RN) 和Flutter,Lynx具有独特的特性. 首先,Lynx采用轻量级 ...
- 自实现模态对话框-DoModal函数
参考CDialog::DoModal函数的实现方式,自己实现了模态框相关功能. ModalBase.h头文件 1 #include <afxwin.h> 2 3 #define ID_NU ...
- 重磅消息,微软宣布 VS Code Copilot 开源,剑指 Cursor!
前言 微软宣布重磅消息将把 GitHub Copilot Chat 扩展的代码以 MIT 许可证协议开源,然后将扩展中的 AI 功能重构到 VS Code 核心中,这一举措是为了将 VS Code 成 ...
- 异步日志分析:MongoDB与FastAPI的高效存储揭秘
title: 异步日志分析:MongoDB与FastAPI的高效存储揭秘 date: 2025/05/22 17:04:56 updated: 2025/05/22 17:04:56 author: ...
- 新纪元:"老"新人
博客园注册很久了,但从未发布过内容.终于开通博客,记录自己,也支持博客园! 另外,这次苹果秋季发布会真的好无聊!︎
- React Native开发鸿蒙Next---RN键盘问题
.markdown-body { line-height: 1.75; font-weight: 400; font-size: 16px; overflow-x: hidden; color: rg ...
- Qt图像处理技术一:对QImage图片美颜,使用双指数滤波
一.效果图 二.demo源码地址(除了磨皮还有一些基本的滤镜) 如果你觉得有用的话,期待你的小星星 实战应用项目: github :https://github.com/dependon/simple ...
- Java线程状态和状态切换
背景 先来探讨一个关于多线程的基础知识:java线程有多少种状态?根据JDK定义,答案是六种!为什么很多人给出的答案却是五种呢?这极有可能是将操作系统层面的线程状态和Java线程状态混为一谈了.因 ...
- Java Solon v3.3.2 发布(可替换,美国博通公司的 Spring 方案)
Solon 框架! Solon 是新一代,Java 企业级应用开发框架.从零开始构建(No Java-EE),有灵活的接口规范与开放生态.采用商用友好的 Apache 2.0 开源协议,是" ...
- 记录.Net 8 发布增加 PublishTrimmed 裁剪选项,调用WMI 的ManagementObject 异常
最近在做OTA的功能,需要获取到sn做一些业务的逻辑.我们自己实现的库里边的,大部分都是调用 System.Management 的 ManagementObjectSearcher 获取 Bios ...