洛谷 P3380 【模板】二逼平衡树(树套树)-线段树套splay
P3380 【模板】二逼平衡树(树套树)
题目描述
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:
查询k在区间内的排名
查询区间内排名为k的值
修改某一位值上的数值
查询k在区间内的前驱(前驱定义为严格小于x,且最大的数,若不存在输出-2147483647)
查询k在区间内的后继(后继定义为严格大于x,且最小的数,若不存在输出2147483647)
注意上面两条要求和tyvj或者bzoj不一样,请注意
输入输出格式
输入格式:
第一行两个数 n,m 表示长度为n的有序序列和m个操作
第二行有n个数,表示有序序列
下面有m行,opt表示操作标号
若opt=1 则为操作1,之后有三个数l,r,k 表示查询k在区间[l,r]的排名
若opt=2 则为操作2,之后有三个数l,r,k 表示查询区间[l,r]内排名为k的数
若opt=3 则为操作3,之后有两个数pos,k 表示将pos位置的数修改为k
若opt=4 则为操作4,之后有三个数l,r,k 表示查询区间[l,r]内k的前驱
若opt=5 则为操作5,之后有三个数l,r,k 表示查询区间[l,r]内k的后继
输出格式:
对于操作1,2,4,5各输出一行,表示查询结果
输入输出样例
9 6
4 2 2 1 9 4 0 1 1
2 1 4 3
3 4 10
2 1 4 3
1 2 5 9
4 3 9 5
5 2 8 5
2
4
3
4
9
说明
时空限制:2s,128M
n,m \leq 5\cdot {10}^4n,m≤5⋅104 保证有序序列所有值在任何时刻满足 [0, {10} ^8][0,108]
题目来源:bzoj3196 / Tyvj1730 二逼平衡树,在此鸣谢
此数据为洛谷原创。(特别提醒:此数据不保证操作4、5一定存在,故请务必考虑不存在的情况)
思路:把普通平衡树代码中rtrt改成rt[pos],写一个线段树,线段树每个点都挂一个splay。
数据范围开22倍正好能过,写的时候老是RE和MLE,最后就是试数据范围过了。。。
代码(自己的板子没写过,百度的其他人的板子):
#include<bits/stdc++.h>
using namespace std;
const int N=5e4*,inf=;
#define lc ch[0]
#define rc ch[1]
int sz[N],fa[N],cnt[N],a[N],ans,mid,l,r,ll,rr,k,rt[N],tot,val[N],ch[][N],n,m,i,opt;
inline char gc(){
static char buf[],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,,,stdin),p1==p2)?EOF:*p1++;
}
inline int rd(){
int x=,fl=;char ch=gc();
for (;ch<||ch>;ch=gc())if(ch=='-')fl=-;
for (;<=ch&&ch<=;ch=gc())x=(x<<)+(x<<)+(ch^);
return x*fl;
}
inline void wri(int a){if(a<)a=-a,putchar('-');if(a>=)wri(a/);putchar(a%|);}
inline void wln(int a){wri(a);puts("");}
//---------------------------------------------------------平衡树
void upd(int x){sz[x]=sz[lc[x]]+sz[rc[x]]+cnt[x];}
void rot(int x){
int y=fa[x],z=fa[y];bool f=x==rc[y];
if (z) ch[y==rc[z]][z]=x;
fa[x]=z,fa[y]=x,fa[ch[!f][x]]=y,ch[f][y]=ch[!f][x],ch[!f][x]=y;
upd(y);
}
void splay(int pos,int x){
while (fa[x]){
int y=fa[x],z=fa[y];
if (z) rot((y==rc[z])^(x==rc[y])?x:y);
rot(x);
}
upd(x);
rt[pos]=x;
}
void ins(int pos,int x){
int t=rt[pos],ff=;
while (t){
sz[t]++;
if (x==val[t]){
cnt[t]++;
return;
}
ff=t,t=ch[x>val[t]][t];
}
t=++tot;
ch[x>val[ff]][ff]=t;
fa[t]=ff,val[t]=x,lc[t]=rc[t]=,sz[t]=cnt[t]=;
splay(pos,t);
}
int find(int pos,int x){
int t=rt[pos];
do{
if (x==val[t]) break;
t=ch[x>val[t]][t];
}while ();
splay(pos,t);
return t;
}
int Rank(int pos,int x){
int t=rt[pos],ret=;
while (t){
if (x==val[t]) return ret+sz[lc[t]];
if (x>val[t]){
ret+=sz[lc[t]]+cnt[t];
t=rc[t];
}else t=lc[t];
}
return ret;
}
//int Max(int x){return rc[x]?Max(rc[x]):x;}
int Max(int t){while (rc[t]) t=rc[t];return t;}
void del(int pos,int x){
x=find(pos,x);
int ls=lc[x],rs=rc[x];
if (--cnt[x]) return;
if (!ls && !rs){//不能直接return,先清空一下
int &k=rt[pos];
k=fa[k]=lc[k]=rc[k]=sz[k]=cnt[k]=val[k]=;
return;
}
if (!ls) rt[pos]=rc[x],fa[rt[pos]]=;
else if (!rs) rt[pos]=lc[x],fa[rt[pos]]=;
else{
int lson=Max(ls);swap(ls,lson);
fa[lson]=,splay(pos,ls),rc[ls]=rs,fa[rs]=ls,upd(ls);
}
}
/*int PRE(int t,int x){
if (!t) return 0;
if (x<=val[t]) return PRE(lc[t],x);
int tmp=PRE(rc[t],x);
return tmp?tmp:t;
}
int pre(int pos,int x){return val[PRE(rt[pos],x)];}*/
int pre(int pos,int x){
int t=rt[pos];int ans=-inf;
while (t)
if (x>val[t]) ans=max(ans,val[t]),t=rc[t];
else t=lc[t];
return ans;
}
int nxt(int pos,int x){
int t=rt[pos];int ans=inf;
while (t)
if (x<val[t]) ans=min(ans,val[t]),t=lc[t];
else t=rc[t];
return ans;
}
//---------------------------------------------------------线段树
#define mid ((l+r)>>1)//如果没加,这个mid就变二分里的mid了
void insert(int t,int l,int r,int x,int v){
ins(t,v);//和下面的修改一样,我刚开始是在l==r的时候插入的
if (l==r) return;
if (x<=mid) insert(t<<,l,mid,x,v);
else insert(t<<|,mid+,r,x,v);
//函数开始的ins()作用跟update()差不多
}
void change(int t,int l,int r,int x,int v){
del(t,a[x]),ins(t,v);//刚开始写的是del(t,v)
if (l==r) return;
if (x<=mid) change(t<<,l,mid,x,v);
else change(t<<|,mid+,r,x,v);
}
int query(int t,int l,int r,int x,int y,int v){
if (x<=l && r<=y) return Rank(t,v);
// if (x<=l && r<=y) return sz[lc[find(t,v)]];----会有不存在v的情况,find()会死循环
int ans=;
if (x<=mid) ans+=query(t<<,l,mid,x,y,v);
if (mid<y) ans+=query(t<<|,mid+,r,x,y,v);
return ans;
}
int Prev(int t,int l,int r,int x,int y,int v){
if (x<=l && r<=y) return pre(t,v);
int ans=-inf;
if (x<=mid) ans=max(ans,Prev(t<<,l,mid,x,y,v));
if (mid<y) ans=max(ans,Prev(t<<|,mid+,r,x,y,v));
return ans;
}
int Succ(int t,int l,int r,int x,int y,int v){
if (x<=l && r<=y) return nxt(t,v);
int ans=inf;
if (x<=mid) ans=min(ans,Succ(t<<,l,mid,x,y,v));
if (mid<y) ans=min(ans,Succ(t<<|,mid+,r,x,y,v));
return ans;
}
#undef mid
int main(){
n=rd(),m=rd();
for (i=;i<=n;i++) a[i]=rd(),insert(,,n,i,a[i]);
for (;m--;){
opt=rd(),l=rd();
if (opt!=) r=rd();
k=rd();
if (opt==) wln(query(,,n,l,r,k)+);
if (opt==){
ll=,rr=1e8;
while (ll<=rr){
mid=ll+rr>>;
if (query(,,n,l,r,mid)+<=k) ans=mid,ll=mid+;
else rr=mid-;
/*if (query(1,1,n,l,r,mid)+1<k) ll=mid+1;------比如查询k=1时query()+1一定>=k,会导致不断向左,输出0
else ans=mid,rr=mid-1;
其实两者本质差别是query()+1==k的时候应该向左还是向右,
如果是小于号的话,rr不断向左,query()也变小,导致答案和标程差很多*/
}
wln(ans);
}
if (opt==) change(,,n,l,k),a[l]=k;//a[l]=k不能忘
if (opt==) wln(Prev(,,n,l,r,k));
if (opt==) wln(Succ(,,n,l,r,k));
}
}
洛谷 P3380 【模板】二逼平衡树(树套树)-线段树套splay的更多相关文章
- BZOJ3196 & 洛谷3380:二逼平衡树——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=3196 https://www.luogu.org/problemnew/show/P3380 (题 ...
- 洛谷P4770 [NOI2018]你的名字 [后缀自动机,线段树合并]
传送门 思路 按照套路,直接上后缀自动机. 部分分:\(l=1,r=|S|\) 首先把\(S\)和\(T\)的后缀自动机都建出来. 考虑枚举\(T\)中的右端点\(r\),查询以\(r\)结尾的串最长 ...
- 洛谷P4254 [JSOI2008]Blue Mary开公司(李超线段树)
题面 传送门 题解 李超线段树板子 具体可以看这里 //minamoto #include<bits/stdc++.h> #define R register #define fp(i,a ...
- 洛谷P4493 [HAOI2018]字串覆盖(后缀自动机+线段树+倍增)
题面 传送门 题解 字符串就硬是要和数据结构结合在一起么--\(loj\)上\(rk1\)好像码了\(10k\)的样子-- 我们设\(L=r-l+1\) 首先可以发现对于\(T\)串一定是从左到右,能 ...
- 【洛谷 P2464】[SDOI2008]郁闷的小J(线段树)
题目链接 这题我很久之前用分块写过,没写出来.. 今天又看到了,于是下决心把这题做出来. 这次我用线段树写的,直接对每本书的编号Hash一下然后离散化然后各建一棵线段树,维护当前编号在某个位置有没有书 ...
- P3369 【模板】普通平衡树(权值线段树)
原来线段树还有这种操作(开成一个桶) 用区间维护在这个区间内元素的个数,离散化一下,居然能达到splay的效果 不仅码量大大减少,而且跑的飞快!!! 6种操作 200多ms 插入 xx 数 删除 x ...
- 洛谷P4770 [NOI2018]你的名字(后缀自动机+线段树)
传送门 我有种自己根本没学过SAM的感觉……最后还是抄了老半天的题解…… 首先,对$S$和每一次的$T$都建一个SAM 先考虑一下$l=1,r=\left| S \right|$的情况 设$lim_i ...
- 洛谷P4364 [九省联考2018]IIIDX 【线段树】
题目 [题目背景] Osu听过没?那是Konano最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐游戏.现在 ,他在世界知名游戏公司KONMAI内工作,离他的梦想也越来越近了.这款 ...
- 洛谷 P4292 - [WC2010]重建计划(长链剖分+线段树)
题面传送门 我!竟!然!独!立!A!C!了!这!道!题!incredible! 首先看到这类最大化某个分式的题目,可以套路地想到分数规划,考虑二分答案 \(mid\) 并检验是否存在合法的 \(S\) ...
- 洛谷 P6071 『MdOI R1』Treequery(LCA+线段树+主席树)
题目链接 题意:给出一棵树,有边权,\(m\) 次询问,每次给出三个数 \(p,l,r\),求边集 \(\bigcap\limits_{i=l}^rE(p,i)\) 中所有边的权值和. 其中 \(E( ...
随机推荐
- 科普帖:Linux操作系统
使用计算机必然会接触操作系统,现代操作系统已经发展的十分成熟,一般用户都可以很轻松的使用计算机.然而,对于要利用计算机进行专业开发和应用的用户来说,需要更加深入地理解操作系统的原理和运行机制,这样才能 ...
- 【转载】C#中使用double.Parse方法将字符串转换为双精度double类型
在C#编程过程中,很多时候涉及到数据类型的转换,例如将字符串类型的变量转换为双精度浮点类型double就是一个常见的类型转换操作,double.Parse方法是C#中专门用来将字符串转换为double ...
- python day 15: IO多路复用,socketserver源码培析,
目录 python day 15 1. IO多路复用 2. socketserver源码分析 python day 15 2019/10/20 学习资料来自老男孩教育 1. IO多路复用 ''' I/ ...
- 《区块链DAPP开发入门、代码实现、场景应用》笔记5——区块链福利彩票的设计
笔者一直强调,一定要利用区块链的特点来解决行业存在的问题,并且该问题最好用区块链解决或者说只能用区块链解决.彩票行业就是个例子. 在讲解代码之前,首先讲解一下业务设计,如图6.15所示. 图6.15 ...
- 在Spring中使用AspectJ实现AOP
在Spring中,最常用的AOP框架是AspectJ,使用AspectJ实现AOP有2种方式: 基于XML的声明式AspectJ 基于注解的声明式AspectJ 基于XML的声明式AspectJ 1. ...
- nginx使用过程中遇到的问题及基本使用总结
问题: 1.出现这个问题nginx: [error] open() "/run/nginx.pid" failed (2: No such file or directory) 解 ...
- Linux命令——jobs、bg、fg、nohup
参考:Bash基础——工作管理(Job control) jobs -l :除了列出 job number 与命令串之外,同时列出 PID 的号码: -r :仅列出正在背景 run 的工作:-s :仅 ...
- 使用opencv去操作树莓派摄像头保存图片和视频
利用树莓派的摄像头去学习opencv的基本操作 —— 保存图片和视频 1.使用Opencv去控制树莓派的摄像头拍照并保存到本地,主要使用cv2和numpy库 #!/usr/bin/python3 # ...
- Python入门篇-面向对象概述
Python入门篇-面向对象概述 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.语言的分类 面向机器 抽象成机器指令,机器容易理解 代表:汇编语言 面向过程 做一件事情,排出个 ...
- Linux服务-rsync
目录 1. rsync简介 2. rsync特性 3. rsync的ssh认证协议 4. rsync命令 5. rsync+inotify Linux服务-rsync 1. rsync简介 rsync ...