BZOJ3196 & 洛谷3380:二逼平衡树——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=3196
https://www.luogu.org/problemnew/show/P3380
(题面用洛谷的)
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:
查询k在区间内的排名
查询区间内排名为k的值
修改某一位值上的数值
查询k在区间内的前驱(前驱定义为严格小于x,且最大的数,若不存在输出-2147483647)
查询k在区间内的后继(后继定义为严格大于x,且最小的数,若不存在输出2147483647)
参考:https://blog.csdn.net/clove_unique/article/details/51279573
其实看参考博客就行了,线段树套splay极限卡时间。
简单解释一下第二种操作:
因为线段树没法查这个,所以一种直观的想法是二分答案。
但由于周多麻烦的因素,我们存所有元素再排个序的效率并不高,于是我们直接对值域二分。
但是显然有些值在这里面是没有的,所以要有一种很妙的判断手段。
我们对于mid求出它在区间内之前有多少值,和k比较,如果比k小就不要。
这样最后我们应该能够找到比该元素小的数有k+1个且最小的元素,且并不难证明这个元素-1的值一定在树中有。
(因为这个元素-1如果不存在的话就应该有k+1个比它小,和“最小的元素”矛盾。)
(人生第一棵树套树,感觉良好,卡常卡的质疑人生。)
#include<cstdio>
#include<algorithm>
using namespace std;
const int INF=;
const int N=4e6+;
inline int read(){
int X=,w=;char ch=;
while(ch<''||ch>''){w|=ch=='-';ch=getchar();}
while(ch>=''&&ch<='')X=(X<<)+(X<<)+(ch^),ch=getchar();
return w?-X:X;
}
int s[N],maxn,rt[N],sz;
int fa[N],tr[N][],key[N],size[N],cnt[N];
inline bool get(int x){
return tr[fa[x]][]==x;
}
inline void clear(int x){
fa[x]=tr[x][]=tr[x][]=key[x]=cnt[x]=size[x]=;
}
inline void splay_upd(int x){
if(!x)return;
size[x]=cnt[x];
if(tr[x][])size[x]+=size[tr[x][]];
if(tr[x][])size[x]+=size[tr[x][]];
}
inline void rotate(int x){
int y=fa[x],z=fa[y],which=get(x);
tr[y][which]=tr[x][which^];fa[tr[y][which]]=y;
fa[y]=x;tr[x][which^]=y;fa[x]=z;
if(z)tr[z][tr[z][]==y]=x;
splay_upd(y);splay_upd(x);
}
inline void splay(int i,int x){
int f=fa[x];
while(f){
if(fa[f])rotate(get(x)==get(f)?f:x);
rotate(x);f=fa[x];
}
rt[i]=x;
}
inline void splay_ins(int i,int v){
if(!rt[i]){
rt[i]=++sz;
fa[sz]=tr[sz][]=tr[sz][]=;
size[sz]=cnt[sz]=;key[sz]=v;
return;
}
int now=rt[i],f=;
while(){
if(key[now]==v){
cnt[now]++;splay_upd(f);splay(i,now);
return;
}
f=now;now=tr[now][key[now]<v];
if(!now){
++sz;
fa[sz]=f;tr[sz][]=tr[sz][]=;
size[sz]=cnt[sz]=;key[sz]=v;
tr[f][key[f]<v]=sz;
splay_upd(f);splay(i,sz);
return;
}
}
}
inline int splay_find(int i,int v){//查询比v小的数的个数
int ans=,now=rt[i];
while(){
if(!now)return ans;
if(v<key[now])now=tr[now][];
else{
ans+=(tr[now][]?size[tr[now][]]:);
if(v==key[now]){
splay(i,now);
return ans;
}
ans+=cnt[now];
now=tr[now][];
}
}
}
inline int splay_pre(int i){
int now=tr[rt[i]][];
while(tr[now][])now=tr[now][];
return now;
}
inline int splay_nxt(int i){
int now=tr[rt[i]][];
while(tr[now][])now=tr[now][];
return now;
}
inline void splay_del(int i,int x){
splay_find(i,x);
if(cnt[rt[i]]>){
cnt[rt[i]]--;return;
}
if(!tr[rt[i]][]&&!tr[rt[i]][]){
clear(rt[i]);rt[i]=;return;
}
if(!tr[rt[i]][]){
int oldroot=rt[i];rt[i]=tr[rt[i]][];fa[rt[i]]=;clear(oldroot);return;
}
else if(!tr[rt[i]][]){
int oldroot=rt[i];rt[i]=tr[rt[i]][];fa[rt[i]]=;clear(oldroot);return;
}
int leftbig=splay_pre(i),oldroot=rt[i];
splay(i,leftbig);
fa[tr[oldroot][]]=rt[i];
tr[rt[i]][]=tr[oldroot][];
clear(oldroot);
splay_upd(rt[i]);
}
inline void seg_mdy(int a,int l,int r,int x,int v){
splay_del(a,s[x]);splay_ins(a,v);
if(l==r)return;
int mid=(l+r)>>;
if(x<=mid)seg_mdy(a<<,l,mid,x,v);
else seg_mdy(a<<|,mid+,r,x,v);
}
inline int seg_find(int a,int l,int r,int l1,int r1,int v){
if(r<l1||r1<l)return ;
if(l1<=l&&r<=r1)return splay_find(a,v);
int mid=(l+r)>>;
return seg_find(a<<,l,mid,l1,r1,v)+seg_find(a<<|,mid+,r,l1,r1,v);
}
inline int seg_pre(int a,int l,int r,int l1,int r1,int v){
if(r<l1||r1<l)return -INF;
if(l1<=l&&r<=r1){
splay_ins(a,v);
int tmp=splay_pre(a);
splay_del(a,v);
return !tmp?-INF:key[tmp];
}
int mid=(l+r)>>;
return max(seg_pre(a<<,l,mid,l1,r1,v),seg_pre(a<<|,mid+,r,l1,r1,v));
}
inline int seg_nxt(int a,int l,int r,int l1,int r1,int v){
if(r<l1||r1<l)return INF;
if(l1<=l&&r<=r1){
splay_ins(a,v);
int tmp=splay_nxt(a);
splay_del(a,v);
return !tmp?INF:key[tmp];
}
int mid=(l+r)>>;
return min(seg_nxt(a<<,l,mid,l1,r1,v),seg_nxt(a<<|,mid+,r,l1,r1,v));
}
inline void seg_build(int a,int l,int r){
for(int i=l;i<=r;i++)splay_ins(a,s[i]);
if(l==r)return;
int mid=(l+r)>>;
seg_build(a<<,l,mid);seg_build(a<<|,mid+,r);
}
int main(){
int n=read(),m=read();
for(int i=;i<=n;++i){
s[i]=read(),maxn=max(maxn,s[i]);
}
seg_build(,,n);
for(int i=;i<=m;++i){
int op=read();
if(op==){
int l=read(),r=read(),k=read();
printf("%d\n",seg_find(,,n,l,r,k)+);
}
if(op==){
int l=read(),r=read(),k=read();
int l1=,r1=maxn+;
while(l1<r1){
int mid=(l1+r1)>>;
int rk=seg_find(,,n,l,r,mid);
if(rk<k)l1=mid+;
else r1=mid;
}
printf("%d\n",l1-);
}
if(op==){
int pos=read(),k=read();
seg_mdy(,,n,pos,k);
s[pos]=k;maxn=max(maxn,s[pos]);
}
if(op==){
int l=read(),r=read(),k=read();
printf("%d\n",seg_pre(,,n,l,r,k));
}
if(op==){
int l=read(),r=read(),k=read();
printf("%d\n",seg_nxt(,,n,l,r,k));
}
}
return ;
}
+++++++++++++++++++++++++++++++++++++++++++
+本文作者:luyouqi233。 +
+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/ +
+++++++++++++++++++++++++++++++++++++++++++
BZOJ3196 & 洛谷3380:二逼平衡树——题解的更多相关文章
- 洛谷P3380 二逼平衡树
线段树+平衡树 我!又!被!卡!常!了! 以前的splay偷懒的删除找前驱后继的办法被卡了QAQ 放一个在洛谷开O2才能过的代码..我太菜了.. #include <bits/stdc++.h& ...
- [luogu3380][bzoj3196]【模板】二逼平衡树【树套树】
题目地址 [洛谷传送门] 题目大意 区间查询k的排名,查找k排名的数,单点修改,区间前驱,区间后继. 感想 真的第一次写树套树,整个人都不对了.重构代码2次,发现样例都过不了,splay直接爆炸,可能 ...
- 【BZOJ3196】Tyvj 1730 二逼平衡树
Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在区间内的 ...
- 【bzoj3196】Tyvj 1730 二逼平衡树 线段树套Treap
题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在区间内的前驱(前驱定义 ...
- 洛谷 P2015 二叉苹果树 题解
题面 裸的树上背包: 设f[u][i]表示在以u为子树的树种选择i条边的最大值,则:f[u][i]=max(f[u][i],f[u][i-j-1]+f[v][k]+u到v的边权); #include ...
- 洛谷 P3380 bzoj3196 Tyvj1730 【模板】二逼平衡树(树套树)
[模板]二逼平衡树(树套树) 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 查询k在区间内的排名 查询区间内排名为k的值 修改某一位值上的数值 查询k在 ...
- 洛谷P3380 【模板】二逼平衡树(树套树)(线段树+树状数组)
P3380 [模板]二逼平衡树(树套树) 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 查询k在区间内的排名 查询区间内排名为k的值 修改某一位值上的数 ...
- 洛谷 P3380 【模板】二逼平衡树(树套树)-线段树套splay
P3380 [模板]二逼平衡树(树套树) 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 查询k在区间内的排名 查询区间内排名为k的值 修改某一位值上的数 ...
- 【题解】二逼平衡树 [P3380] [BZOJ3196] [Tyvj1730]
[题解]二逼平衡树 [P3380] [BZOJ3196] [Tyvj1730] 传送门:[模板]二逼平衡树(树套树)\([P3380]\) \([BZOJ3196]\) \([TYVJ1730]\) ...
随机推荐
- libevent学习五(Helper functions and types for Libevent)
基础类型 #ifdef WIN32 #define evutil_socket_t intptr_t #else #define evutil_socket_t int #endif ev_ssi ...
- Android Studio怎样创建App项目
然后等待大约N分钟: 默认的是Android模式, 改为Project模式更符合我们的习惯:
- Java enum类型笔记
用途: 定义命令行参数,菜单选项,星期,方向(东西南北)等 与普通类的不同 有默认的方法 value() 每个enum类都已默认继承java.lang.Enum,所以enum类不能继承其他类 构造方法 ...
- 前端开发工程师 - 01.页面制作 - 第4章.CSS
第4章.CSS CSS简介 Cascading Style Sheet 层叠样式表:定义页面中的表现样式 history: CSS1(1996)--CSS2(1998)--着手CSS3草案(拆分成很多 ...
- [SHELL]结构化命令之条件语句
1.if-then语句 #!/bin/bash username="root" if grep $username /etc/passwd then echo "the ...
- 【转】网游服务器中的GUID(唯一标识码)实现-基于snowflake算法
本文中的算法采用twitter的snowflake算法,具体请搜索介绍,原来是用Scala写的,因我项目需要,改写成C++语言,主要用于高效的生成唯一的ID, 核心算法就是毫秒级时间(41位)+机器I ...
- 【Python 开发】第三篇:python 实用小工具
一.快速启动一个web下载服务器 官方文档:https://docs.python.org/2/library/simplehttpserver.html 1)web服务器:使用SimpleHTTPS ...
- 剑指offer-整数中1出现的次数27
题目描述 求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1.10.11.12.13因此共出现6次,但是对于后面问题他就没辙了. ...
- UVa 1586 - Molar Mass - ACM/ICPC Seoul 2007 - C语言
关键在于判断数字是两位数还是单位数,其他部分没有难度. #include"stdio.h" #include"string.h" #include"c ...
- 一些容易记混的c++相关知识点
一些容易记混的c++相关知识. 截图自:<王道程序员面试宝典>