P3380 【模板】二逼平衡树(树套树)

题目描述

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

  1. 查询k在区间内的排名

  2. 查询区间内排名为k的值

  3. 修改某一位值上的数值

  4. 查询k在区间内的前驱(前驱定义为严格小于x,且最大的数,若不存在输出-2147483647)

  5. 查询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各输出一行,表示查询结果

输入输出样例

输入样例#1: 复制

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
输出样例#1: 复制

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的更多相关文章

  1. BZOJ3196 & 洛谷3380:二逼平衡树——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=3196 https://www.luogu.org/problemnew/show/P3380 (题 ...

  2. 洛谷P4770 [NOI2018]你的名字 [后缀自动机,线段树合并]

    传送门 思路 按照套路,直接上后缀自动机. 部分分:\(l=1,r=|S|\) 首先把\(S\)和\(T\)的后缀自动机都建出来. 考虑枚举\(T\)中的右端点\(r\),查询以\(r\)结尾的串最长 ...

  3. 洛谷P4254 [JSOI2008]Blue Mary开公司(李超线段树)

    题面 传送门 题解 李超线段树板子 具体可以看这里 //minamoto #include<bits/stdc++.h> #define R register #define fp(i,a ...

  4. 洛谷P4493 [HAOI2018]字串覆盖(后缀自动机+线段树+倍增)

    题面 传送门 题解 字符串就硬是要和数据结构结合在一起么--\(loj\)上\(rk1\)好像码了\(10k\)的样子-- 我们设\(L=r-l+1\) 首先可以发现对于\(T\)串一定是从左到右,能 ...

  5. 【洛谷 P2464】[SDOI2008]郁闷的小J(线段树)

    题目链接 这题我很久之前用分块写过,没写出来.. 今天又看到了,于是下决心把这题做出来. 这次我用线段树写的,直接对每本书的编号Hash一下然后离散化然后各建一棵线段树,维护当前编号在某个位置有没有书 ...

  6. P3369 【模板】普通平衡树(权值线段树)

    原来线段树还有这种操作(开成一个桶) 用区间维护在这个区间内元素的个数,离散化一下,居然能达到splay的效果 不仅码量大大减少,而且跑的飞快!!! 6种操作  200多ms 插入 xx 数 删除 x ...

  7. 洛谷P4770 [NOI2018]你的名字(后缀自动机+线段树)

    传送门 我有种自己根本没学过SAM的感觉……最后还是抄了老半天的题解…… 首先,对$S$和每一次的$T$都建一个SAM 先考虑一下$l=1,r=\left| S \right|$的情况 设$lim_i ...

  8. 洛谷P4364 [九省联考2018]IIIDX 【线段树】

    题目 [题目背景] Osu听过没?那是Konano最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐游戏.现在 ,他在世界知名游戏公司KONMAI内工作,离他的梦想也越来越近了.这款 ...

  9. 洛谷 P4292 - [WC2010]重建计划(长链剖分+线段树)

    题面传送门 我!竟!然!独!立!A!C!了!这!道!题!incredible! 首先看到这类最大化某个分式的题目,可以套路地想到分数规划,考虑二分答案 \(mid\) 并检验是否存在合法的 \(S\) ...

  10. 洛谷 P6071 『MdOI R1』Treequery(LCA+线段树+主席树)

    题目链接 题意:给出一棵树,有边权,\(m\) 次询问,每次给出三个数 \(p,l,r\),求边集 \(\bigcap\limits_{i=l}^rE(p,i)\) 中所有边的权值和. 其中 \(E( ...

随机推荐

  1. 混沌理论(Chaos theory)和非线性系统

    混沌理论(Chaos theory)是关于非线性系统在一定参数条件下展现分岔(bifurcation).周期运动与非周期运动相互纠缠,以至于通向某种非周期有序运动的理论.在耗散系统和保守系统中,混沌运 ...

  2. CentOS -- 新建用户并使能密钥登录

    目录 1. 新建用户 2. 为新用户授权 2.1. 方法一:把新用户添加到wheel用户组中 2.2. 方法二:把新用户添加到sudoers列表中 3. 新用户使能 SSH 密钥登录 4. 其它 4. ...

  3. 30个关于Shell脚本的经典案例(上)

    对于初学者而言,因为没有实战经验,写不出来Shell脚本很正常,如果工作了几年的运维老年还是写不出来,那就是没主动找需求,缺乏练习,缺乏经验.针对以上问题,总结了30个生产环境中经典的Shell脚本, ...

  4. jQuery实现图片上传

    $('input[type="file"]').change(function(event) { var currentTarget = event.currentTarget; ...

  5. Java 之 NOSQL

    一.什么是 NOSQL NoSQL(NoSQL = Not Only SQL),意即“不仅仅是SQL”,是一项全新的数据库理念,泛指非关系型的数据库. 随着互联网web2.0网站的兴起,传统的关系数据 ...

  6. 计算地图上两点间的距离PHP类

    计算地图上两点间的距离,使用的是谷歌地图 <?php class GeoHelper { /** * @param int $lat1 * @param int $lon1 * @param i ...

  7. java requestmapping中关于路径的问题

    需要这种url写的方式才能映射

  8. 为什么说pt-osc可能会引起主从延迟,有什么好办法解决或规避吗?

    若复制中binlog使用row格式,对大表使用pt-osc把数据从旧表拷贝到临时表,期间会产生大量的binlog,从而导致延时 pt-osc在搬数据过程中insert...select是有行锁的,会降 ...

  9. 利用mysql的inet_aton()和inet_ntoa()函数存储IP地址的方法

    原文:https://www.jb51.net/article/29962.htm 当前很多应用都适用字符串char(15)来存储IP地址(占用16个字节),利用inet_aton()和inet_nt ...

  10. HikariCP 个人实例

    pom依赖 <!--HikariCP数据库连接池--> <dependency> <groupId>com.zaxxer</groupId> <a ...