洛谷P3369 普通平衡树之板子

news/2025/1/17 0:55:23/文章来源:https://www.cnblogs.com/qc0817/p/18339992

洛谷P3369题解

传送锚点

摸鱼环节

【模板】普通平衡树

题目描述

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:

  1. 插入一个数 \(x\)
  2. 删除一个数 \(x\)(若有多个相同的数,应只删除一个)。
  3. 定义排名为比当前数小的数的个数 \(+1\)。查询 \(x\) 的排名。
  4. 查询数据结构中排名为 \(x\) 的数。
  5. \(x\) 的前驱(前驱定义为小于 \(x\),且最大的数)。
  6. \(x\) 的后继(后继定义为大于 \(x\),且最小的数)。

对于操作 3,5,6,不保证当前数据结构中存在数 \(x\)

输入格式

第一行为 \(n\),表示操作的个数,下面 \(n\) 行每行有两个数 \(\text{opt}\)\(x\)\(\text{opt}\) 表示操作的序号($ 1 \leq \text{opt} \leq 6 $)

输出格式

对于操作 \(3,4,5,6\) 每行输出一个数,表示对应答案。

样例 #1

样例输入 #1

10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598

样例输出 #1

106465
84185
492737

简单分析下题目,很明显可以选择用平衡树来AC他,一般来说旋转平衡树是基础,但我不会啊FHQtreep明显更有性价比,作为20世纪的好青年,当然是什么简单写什么选择顺应时代的写法,所以这题成为了FHQtreep训练鸡。



正片开始

1. 合理建树

直接选择结构体来处理树。
咱需要:

  • l,r表示左子树和右子树;
  • val存储权值;
  • rnd存储随机权值;
  • size存储当前树的大小;
  • 写一个newnode函数存入新建点;

code:

struct 
{int l,r;int val;int rnd;int size;
}tr[N];
int root=0,n=0;
inline int _rand(){return rnd();}
int newnode(int v)
{tr[++n].val=v;tr[n].rnd=_rand();tr[n].size=1;return n;
}

2.处理更新必要部分

将左右子树更新后加上自己,很简单,但别忘了。

code:

void pushup(int p){tr[p].size=tr[tr[p].l].size+tr[tr[p].r].size+1;}

3.核心操作之分裂合并

  1. 对于分裂,递归处理即可。勿忘更新。
void split(int p,int v,int &x,int &y)
{if(!p) {x=y=0;return;}if(tr[p].val<=v){x=p;split(tr[p].r,v,tr[p].r,y);}else{y=p;split(tr[p].l,v,x,tr[p].l);}pushup(p);
}
  1. 对于合并操作,如果子树为空,直接相加即可。选择小根队,判断下左子树与右子树的随机权值,将较小的作为根即可。记得更新。

code:

int merge(int x,int y)
{if(!x||!y) {return x+y;}if(tr[x].rnd<tr[y].rnd){tr[x].r=merge(tr[x].r,y);pushup(x);return x;}else{tr[y].l=merge(x,tr[y].l);pushup(y);return y;}
}

4. 其余操作处理

太水了,分分合合就行了

  • 增加操作,将树以v分成两个子树,建立一个新的插入点,可视作第三棵子树,然后将三棵子树合并即可。

code:

void insert(int v)
{int x,y;split(root,v,x,y);int z=newnode(v);root=merge(merge(x,z),y);
} 
  • 删除操作只需将树分成三棵子树,并将所需要删除的点的树上左子树和右子树直接合并即可。

code:

void del(int v)
{int x,y,z;split(root,v,x,z);split(x,v-1,x,y);y=merge(tr[y].l,tr[y].r);root=merge(merge(x,y),z);
}
  • 接下来的操作都可以参考上述方法,很容易得出结果。

code:

int rank(int v)
{int x,y;split(root,v-1,x,y);int ans=tr[x].size+1;root=merge(x,y);return ans;
}
int topk(int p,int k)
{int lsz=tr[tr[p].l].size;if(k==lsz+1) return tr[p].val;if(k<=lsz) return topk(tr[p].l,k);return topk(tr[p].r,k-lsz-1);
}
int get_pre(int v)
{int x,y;split(root,v-1,x,y);int ans=topk(x,tr[x].size);root=merge(x,y);return ans;
}
int get_suc(int v)
{int x,y;split(root,v,x,y);int ans=topk(y,1);root=merge(x,y);return ans;
}

函数部分到此结束。


完整代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e6+5;
unsigned long long seed=1;
std::mt19937 rnd(std::random_device{}());
struct fhq
{struct {int l,r;int val;int rnd;int size;}tr[N];int root=0,n=0;inline int _rand(){return rnd();}int newnode(int v){tr[++n].val=v;tr[n].rnd=_rand();tr[n].size=1;return n;}void pushup(int p){tr[p].size=tr[tr[p].l].size+tr[tr[p].r].size+1;}void split(int p,int v,int &x,int &y){if(!p) {x=y=0;return;}if(tr[p].val<=v){x=p;split(tr[p].r,v,tr[p].r,y);}else{y=p;split(tr[p].l,v,x,tr[p].l);}pushup(p);}int merge(int x,int y){if(!x||!y) {return x+y;}if(tr[x].rnd<tr[y].rnd){tr[x].r=merge(tr[x].r,y);pushup(x);return x;}else{tr[y].l=merge(x,tr[y].l);pushup(y);return y;}}explicit fhq() { memset(tr, 0, sizeof tr); }int size() { return tr[root].size; }void insert(int v){int x,y;split(root,v,x,y);int z=newnode(v);root=merge(merge(x,z),y);} void del(int v){int x,y,z;split(root,v,x,z);split(x,v-1,x,y);y=merge(tr[y].l,tr[y].r);root=merge(merge(x,y),z);}int rank(int v){int x,y;split(root,v-1,x,y);int ans=tr[x].size+1;root=merge(x,y);return ans;}int topk(int p,int k){int lsz=tr[tr[p].l].size;if(k==lsz+1) return tr[p].val;if(k<=lsz) return topk(tr[p].l,k);return topk(tr[p].r,k-lsz-1);}int get_pre(int v){int x,y;split(root,v-1,x,y);int ans=topk(x,tr[x].size);root=merge(x,y);return ans;}int get_suc(int v){int x,y;split(root,v,x,y);int ans=topk(y,1);root=merge(x,y);return ans;}
};
int main()
{int n,m;cin>>m;fhq t;int ans=0;while(m--){int op,x;cin>>op>>x;if(op==1) t.insert(x);if(op==2) t.del(x);if(op==3) cout<<t.rank(x)<<endl;if(op==4) cout<<t.topk(t.root,x)<<endl;if(op==5) cout<<t.get_pre(x)<<endl;if(op==6) cout<<t.get_suc(x)<<endl;}return 0;
}


双倍经验之洛谷P6136

传送锚点

题目简述:

【模板】普通平衡树(数据加强版)

题目背景

本题是 P3369 数据加强版,扩大数据范围并增加了强制在线

题目的输入、输出和原题略有不同,但需要支持的操作相同。

题目描述

您需要写一种数据结构(可参考题目标题),来维护一些整数,其中需要提供以下操作:

  1. 插入一个整数 \(x\)
  2. 删除一个整数 \(x\)(若有多个相同的数,只删除一个)。
  3. 查询整数 \(x\) 的排名(排名定义为比当前数小的数的个数 \(+1\))。
  4. 查询排名为 \(x\) 的数(如果不存在,则认为是排名小于 \(x\) 的最大数。保证 \(x\) 不会超过当前数据结构中数的总数)。
  5. \(x\) 的前驱(前驱定义为小于 \(x\),且最大的数)。
  6. \(x\) 的后继(后继定义为大于 \(x\),且最小的数)。

本题强制在线,保证所有操作合法(操作 \(2\) 保证存在至少一个 \(x\),操作 \(4,5,6\) 保证存在答案)。

输入格式

第一行两个正整数 \(n,m\),表示初始数的个数和操作的个数。

第二行 \(n\) 个整数 \(a_1,a_2,a_3,\ldots,a_n\),表示初始的数

接下来 \(m\) 行,每行有两个整数 \(\text{opt}\)\(x'\)\(\text{opt}\) 表示操作的序号($ 1 \leq \text{opt} \leq 6 \(),\)x'$ 表示加密后的操作数。

我们记 \(\text{last}\) 表示上一次 \(3,4,5,6\) 操作的答案,则每次操作的 \(x'\) 都要异或\(\text{last}\) 才是真实的 \(x\)。初始 \(\text{last}\)\(0\)

输出格式

输出一行一个整数,表示所有 \(3,4,5,6\) 操作的答案的异或和

样例 #1

样例输入 #1

6 7
1 1 4 5 1 4
2 1
1 9
4 1
5 8
3 13
6 7
1 4

样例输出 #1

6

提示

样例解释

样例加密前为:

6 7
1 1 4 5 1 4
2 1
1 9
4 1
5 9
3 8
6 1
1 0

限制与约定

对于 \(100\%\) 的数据,\(1\leq n\leq 10^5\)\(1\leq m\leq 10^6\)\(0\leq a_i,x\lt 2^{30}\)

本题输入数据较大,请使用较快的读入方式。


\(\text{upd 2022.7.22}\):新增加 \(9\) 组 Hack 数据。



思路还是一模一样,这里直接给到代码。

#include <cstdio>
#include <cstring>
#include <random>
using namespace std;const int N=1e6+1e5+1;
unsigned long long seed=1;std::mt19937 rnd(std::random_device{}());
struct fhq
{struct{int l,r;int val;int rnd;int size;}tr[N];int root=0,n=0;inline int _rand(){return rnd();}int newnode(int v){tr[++n].val=v;tr[n].rnd=_rand();tr[n].size=1;return n;}void pushup(int x){tr[x].size=tr[tr[x].l].size+tr[tr[x].r].size+1;}void split(int rt,int v,int &x,int &y){if(!rt) {x=y=0;return;}if(tr[rt].val<=v){x=rt;split(tr[rt].r,v,tr[rt].r,y);}else{y=rt;split(tr[rt].l,v,x,tr[rt].l);}pushup(rt);}int merge(int x,int y){if(!x||!y) return x+y;if(tr[x].rnd<tr[y].rnd){tr[x].r=merge(tr[x].r,y);pushup(x);return x;}else{tr[y].l=merge(x,tr[y].l);pushup(y);return y;  }}explicit fhq(){memset(tr,0,sizeof tr);}int size(){return tr[root].size;}void insert(int v){int x,y;split(root,v,x,y);int z=newnode(v);root=merge(merge(x,z),y);}void del(int v){int x,y,z;split(root,v,x,z);split(x,v-1,x,y);y=merge(tr[y].l,tr[y].r);root=merge(merge(x,y),z);}int rank(int v){int x,y;split(root,v-1,x,y);int ans=tr[x].size+1;root=merge(x,y);return ans;}int topk(int rt,int k){int lsz=tr[tr[rt].l].size;if(k==lsz+1) return tr[rt].val;if(k<=lsz) return topk(tr[rt].l,k);return topk(tr[rt].r,k-lsz-1);}int get_pre(int v){int x,y;split(root,v-1,x,y);int ans=topk(x,tr[x].size);root=merge(x,y);return ans;}int get_suc(int v){int x,y;split(root,v,x,y);int ans=topk(y,1);root=merge(x,y);return ans;}
};
int main(void)
{int n,m;scanf("%d%d",&n,&m);    fhq t;int w;for(int i=0;i<n;i++) {scanf("%d", &w);t.insert(w);}int ans=0,last=0;while(m--){int op,x;scanf("%d%d", &op, &x);x^=last;if(op==1) t.insert(x);if(op==2) t.del(x);if(op==3) {last=t.rank(x);ans^=last;}if(op==4) {last=t.topk(t.root,x);ans^=last;}if(op==5) {last=t.get_pre(x);ans^=last;}if(op==6) {last=t.get_suc(x);ans^=last;}}printf("%d\n", ans);return 0;
}

本蒟蒻只能写到这。
谢谢!

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

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

相关文章

读零信任网络:在不可信网络中构建安全系统07设备信任

设备安全1. 设备信任 1.1. 在零信任网络中建立设备信任至关重要,这也是非常困难的一个环节 1.2. 建立设备信任是基石,直接影响零信任网络架构的成败 1.3. 大多数网络安全事件都和攻击者获得信任设备的控制权相关,这种情况一旦发生,信任就将被彻底瓦解,无法通过设备来确保安…

GPGPU技术杂谈

GPGPU技术杂谈1.GPGPU行业概述GPU最初的设计目标是为了提升计算机对图形、图像、视频等数据的处理性能,解决CPU在图形图像领域处理效率低的难题。随着GPU在并行计算方面性能优势的逐步显现以及并行计算应用范围的逐步拓展,GPU逐渐分化成两条分支,一条是传统意义的GPU,延续专…

C++编译错误的正确查找方式

C++编译错误的正确查找方式 在VS2019或者2022中,代码发生错误: 1、自己排查 2、自己排除不了,就复制错误的代码提示到浏览器查看,然后看到的是CSDN文章 推荐去编译器官网查找,你会发现新大陆!查找编译器错误链接: https://learn.microsoft.com/zh-cn/cpp/error-messages…

音视频相关

1.音视频相关 1.一些基本数据格式封装格式:MP4,RMVB,TS,FLV,AVI 视频编码数据:H.264,MPEG2,VC-1 音频编码数据:AAC,MP3,AC-3 视频像素数据:YUV420P,RGB 音频采样数据:PCM2.播放视频文件的流程 3.编码格式及工具 1)封装格式:视频码流和音频码流按照一定的格式存储在一个文…

伙伴匹配系统踩坑日记2

伙伴匹配系统踩坑日记2 Time:2024.8.2 后端构建 复制一份之前的用户中心后端项目,改名,删去原来的.idea和.mvn,重启idea会提示 maven重构往后写发现不需要用新的项目,直接在原来的用户中心里加功能就行 新建标签表 create table tag (id bigint auto_increment com…

2024 年上海新能源汽车消费补贴 All In One

2024 年上海新能源汽车消费补贴 All In One2024 年上海新能源汽车消费补贴 All In One2024年“上海之夏”汽车消费嘉年市商务委发布国家报废更新补贴和本市置换更新补贴政策。 一是落实国家汽车以旧换新新政策。按照国家实施汽车以旧换新的统一部署,2024年对个人消费者对报废国…

全网最适合入门的面向对象编程教程:30 Python的内置数据类型-object根类

在 Python 中,所有的类都直接或间接继承自一个根类,这个根类是Object。Object类是 Python 中所有新式类的基础类,在 Python 的类层次结构中,Object类是所有类的最终基类。全网最适合入门的面向对象编程教程:30 Python 的内置数据类型-object 根类摘要: 在 Python 中,所有…

javascript js WebGL WebGL2 后期处理特效之点击水波纹涟漪例子

先来看结果图(转.gif掉帧了): 完整源码分享网址: https://share.weiyun.com/Vpkp5KP31 首先初始化用到的所有图片:1 const images = [2 "./img/girls.jpg",3 "./img/ball.png",4 "./img/water.jpg", 5 "./img/spriteX8.pn…

DC-3.2靶机详解

DC-3.2 信息搜集 IP 探测 arp-scan -l nmap -sn 192.168.179.0/24 netdiscover -r 192.168.179.0/24目标及 ip 就为 192.168.179.134。 端口探测 nmap -sT --min-rate 10000 -p- 192.168.179.134就开放了一个 80 端口 再来个详细端口扫描和漏洞脚本扫描 nmap -sT -sV -O -p80 1…

PyTorch 训练自定义功能齐全的神经网络模型的详细教程

PyTorch 是一个开源的机器学习框架,可以方便地进行神经网络模型训练和推理。本文基于 PyTorch 演示了一个非常简单但是功能齐全的神经网络训练过程,无论模型权重有多大,使用 TyTorch 训练的过程是类似的,期望本文能启到抛砖引玉的作用……在前面的文章中,老牛同学介绍了不…

【视频讲解】CatBoost、LightGBM和随机森林的海域气田开发特征分类研究

原文链接:https://tecdat.cn/?p=37208 原文出处:拓端数据部落公众号 分析师:Changlin Li 本文将通过视频讲解,展示如何用CatBoost、LightGBM和随机森林的海域气田开发特征智能分类,并结合一个python分类预测职员离职:逻辑回归、梯度提升、随机森林、XGB、CatBoost、LGB…