可持久化权值线段树(主席树)笔记

news/2025/2/15 23:55:15/文章来源:https://www.cnblogs.com/dianman/p/18716351

可持久化权值线段树(主席树)笔记

区别于普通线段树,权值线段树维护的信息不同

  • 普通线段树:节点区间是序列的下标区间,维护区间最值,区间和等信息
  • 权值线段树:节点区间是序列的值域,维护值域内数出现的次数

*图片引自董晓算法


给定一个区间,询问该区间内的第 \(k\) 小值是多少,暴力的方案就是每次都开一颗线段树,由于空间受限我们并不能这样做

一种可行且有效的办法就是建立主席树,保存每次插入时的历史版本,下一次直接从这个版本上衍生节点即可

#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]struct node{int ch[2];//左右儿子int s;//记录值域内的元素个数
};int n,m;
int a[N];
node tr[N*22];
int root[N],idx;

首先是建树过程,与线段树类似,递归创建值域 \([1,n]\) 内的各个节点,以及对节点进行编号

build(root[0],1,n);void build(int &x,int l,int r){x=++idx;//更新编号if (l==r) return;//递归出口int m=l+r>>1;build(lc(x),l,m);//递归建立左子树build(rc(x),m+1,r);//递归建立右子树
}

需要对 \(x\) 传入引用,即进行写入操作,更改节点原有值


插入操作,建立主席树,递归创建各个版本的线段树

insert(root[i-1],root[i],1,n,v);void insert(int x,int &y,int l,int r,int v){//插入vy=++idx;//对节点进行编号tr[y]=tr[x];//复制上个版本的信息到当前版本tr[y].s++;//因为插入了一个数,所以该值域内的元素个数加1if (l==r) return;int m=l+r>>1;//二分if (v<=m) insert(lc(x),lc(y),l,m,v);//依然传入上个版本的儿子与当前版本的引用else insert(rc(x),rc(y),m+1,r,v);
}

x 传入的是上一个版本的线段树的内容,不引用进行只读操作,y 传入的是当前版本来进行建树,写入操作

同样的 lc(x)lc(y) 相当于两个指针,前者指向上一个版本,后者指向当前版本,进行同步搜索


查询操作,查询区间 \([l,r]\) 内的第 \(k\)

考虑普通情况:查询区间 \([1,r]\) 内的第 \(k\) 小,那么找到插入 \(r\) 时的版本在树上进行搜索即可

那么求 \([l,r]\) 上的第 \(k\) 小,可以采取类似前缀和的处理方式,分别查询 \([1,l-1]\)\([1,r]\) 的信息,做差即可

query(root[l-1],root[r],1,n,k);int query(int x,int y,int l,int r,int k){if (l==r) return l;int m=l+r>>1;int s=tr[lc(y)].s-tr[lc(x)].s;if (k<=s) return query(lc(x),lc(y),l,m,k);else return query(rc(x),rc(y),m+1,r,k-s);//减去左子树值域内增加元素的个数
}

其中 int s=tr[lc(y)].s-tr[lc(x)].s; 计算的是两个版本节点对应左子树值域内元素的增加个数

如果左子树值域内增加的个数小于等于我们需要求的第 \(k\) 小,那么这个元素必然在左子树值域内增加的元素中

如果左子树值域内增加的个数大于我们需要求的第 \(k\) 小,那么这个元素必然在右子树值域内增加的元素中


考虑需要处理的数值值域较大但数量较少时,可以采取离散化的操作,一一映射下标

vector<int> v;for (int i=1;i<=n;i++){cin>>a[i];v.push_back(a[i]);
}
sort(v.begin(),v.end());//从小到大排序
v.erase(unique(v.begin(),v.end()),v.end());//去重int getid(int x){//得到离散化后的值,一一映射关系return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}

主席树查询区间内小于等于 \(k\) 的元素个数

int rankquery(int x,int y,int l,int r,int k){if (r<=k) return tr[y].s-tr[x].s;//如果k比区间内所有值都要大,那么直接返回区间内元素总数if (l>k) return 0;//如果k比区间内所有值都要小,那么直接返回区间内元素总数int m=l+r>>1;//二分return rankquery(lc(x),lc(y),l,m,k)+rankquery(rc(x),rc(y),m+1,r,k);//递归左右子树搜索
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/883982.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于排队理论的客户结账等待时间MATLAB模拟仿真

1.程序功能描述 基于排队理论的客户结账等待时间MATLAB模拟仿真,分析平均队长,平均等待时长,不能结账的概率,损失顾客数,到达顾客数,服务顾客数,平均服务时间。 2.测试软件版本以及运行结果展示MATLAB2022A版本运行 3.核心程序figure; plot(mean(mLen_seq,2),-b^…

【牛客训练记录】牛客2025年情人节比赛

训练情况赛后反思 今年比赛比去年有意思多了,太搞笑了 A题 我们构造一对就可以了,和为 \(x\),直接扔上去 \(1\) 和 \(x-1\) 即可点击查看代码 #include <bits/stdc++.h> // #define int long long #define endl \nusing namespace std;void solve(){int x; cin>>…

PVE8.3.1 直通硬盘

直通可以分为全盘直通和硬件直通,硬件直通会将整个 SATA 控制器直通给虚拟机,这可能会导致所有硬盘都分配给了一个虚拟机,这里介绍全盘直通。 1,查看硬盘IDls /dev/disk/by-id如上图ata 开头的设备就表示 sata 硬盘2,硬盘直通qm set 虚拟机id -sata0 /dev/disk/by-id/ata…

CTFCryto01-URL编码

URL编码URL编码,也称为百分号编码,是一种用于在URL(统一资源定位符)中传输特殊字符的编码方式 当 URL 路径或者查询参数中,带有中文或者特殊字符的时候,就需要对 URL 进行编码(采用十六进制编码格式)。URL 编码的原则是使用安全字符去表示那些不安全的字符 安全字符:指…

Qt基于QWidget打造网速小部件SpeedMeterWidget

目标基于QWidget自定义网速小部件 支持设置上行和下行方向 支持自定义设置网速显示数值和单位 支持动态设置上行和下行颜色效果图控件完整代码 SpeedMeterWidget.h #pragma once#include <QWidget> #include <QPainter> class QPaintEvent;/// /// @brief: 速度仪…

Qt基于QWidget实现倒计时控件CircleCountDownBar

目标基于Qwidget实现圆弧倒计时 可自定义圆弧内部显示的文字 可设定当前圆弧的进度效果图完整控件代码 CircleCountDownBar.h #pragma once#include <QWidget> class QPaiter; class QPaintEvent;class CircleCountDownBar : public QWidget {Q_OBJECTpublic:CircleCount…

初学者的量化 “利器”,哪种策略才是你的菜?

初学者量化投资别迷茫!中低频策略为何优选?快来解锁适合拟的量化“利器”策略更多精彩内容,欢迎关注公众号:数量技术宅,也可添加技术宅个人微信号:sljsz01,与我交流。 写在前面 这篇文章专为量化投资领域的小白精心打造,量化大神们请自动略过哦。 本文将从中低频交易方…

Qt实现速度仪表盘GaugeSpeedWidget

目标基于QWidget绘制速度仪表盘, 仪表盘颜色为渐变色, 可指定当前显示的文字内容和文字颜色 可指定当前指针旋转的数值效果图控件完整代码 GaugeSpeedWidget.h #pragma once#include <QWidget> #include <QMap>class QPaintEvent; class QPainter;/// /// @brie…

pcie报文

PCIE的四种请求memory、IO的地址编码posted、no-postedTLP包类型缩写MRD举例TLP包格式TLP HEADTLP HEAD byte0memory操作

P1107 [BJWC2008] 雷涛的小猫

链接 https://www.luogu.com.cn/problem/P1107 题目思路因为在取h+delta的max时会一直遍历,所以直接加个记忆,就不用一直遍历了。 代码 #define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<algorithm> #include<queue> #include<cmath> #in…

PVE8.3.1开启核显虚拟化SR-IOV

本来想集成到前面的文章,但是被网上一些文章误导从而浪费了3H之后决定还是单开一篇专门说说PVE8.3.1开启核显虚拟化的过程。所以建议大家还是参考更有价值的唯一的官方指南 https://github.com/strongtz/i915-sriov-dkms 交代一下背景: 发文日期2025年2月14日,以下所有操作均…