洛谷 P3380 【【模板】二逼平衡树(树套树)】
其实比想象中的好理解啊
所谓树套树,就是在一棵树的基础上,每一个节点再维护一棵树
说白了,就是为了实现自己想要的操作和优秀的时间复杂度,来人为的增加一些毒瘤数据结构来维护一些什么东西
比如说这道题
如果只求一个区间内的一个数是否是最大值或者最小值,我们显然可以用线段树轻轻松松地解决这个问题
但是现在我们要求一个区间内一个数是否是k大值,那么这个东西我们就可以很显然的用平衡树来解决这个问题
即对每一个区间都单独维护一棵平衡树,然后利用线段树的外壳,将得到的各个区间内的答案进行合并
然后,这道题除了码量极大,常数极难卡,好像就没有什么可以做的了
好了,还是来说一下每一个操作的具体做法
操作一,就像上文所说的那样,对每一个与询问区间有关的区间进行求\(rank\)操作,然后合并。
操作二,由于线段树不能够很好的满足我们的要求,所以我们可以选择利用已经写好了的操作一,对这个数进行二分处理,然后对于每一个二分出的数字,进行操作一判定
操作三,暴力修改即可,对于每一棵包含这个位置的平衡树,先将原数删除,再插入新数
操作四&操作五 两种做法
做法一:你可以选择偷懒,先利用操作一,求出其排名,再利用操作二,求出排名-1(+1)的数字
做法二:对于区间中的每一棵平衡树,对其进行求前驱(后继)操作,然后再取最大(最小)值。
完结撒花~
下面是本人为了偷懒写得又丑,常数又大的线段树套Splay的代码:
// luogu-judger-enable-o2
#include<cstdio>
#include<iostream>
#define dir(p) (son[fa[p]][1]==p)
using namespace std;
const int maxn=4e6+10;
int inline read()
{
int num=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
num=num*10+ch-'0';
ch=getchar();
}
return num*f;
}
int n,m;
int siz[maxn],son[maxn][2],fa[maxn],num[maxn],tot[maxn],cnt;
void upd(int t){
siz[t]=siz[son[t][1]]+siz[son[t][0]]+tot[t];
}
void rot(int p){
int fp=fa[p],ffp=fa[fp],way=dir(p);
son[fp][way]=son[p][way^1];
fa[son[p][way^1]]=fp;
son[ffp][dir(fp)]=p;
fa[p]=ffp;
son[p][way^1]=fp;
fa[fp]=p;
upd(fp),upd(p);
}
struct Splay{
int root;
void splay(int p,int g)
{
while(fa[p]!=g)
{
int fp=fa[p],ffp=fa[fp];
if(ffp!=g&&fp!=0)
{
if(dir(fp)==dir(p)) rot(fp);
else rot(p);
}
rot(p);
}
if(g==0) root=p;
}
void find(int x)
{
int u=root;
if(root==0) return ;
while(son[u][x>num[u]]&&x!=num[u])
u=son[u][x>num[u]];
splay(u,0);
}
int pre(int x)
{
find(x);
if(num[root]<x) return root;
int u=son[root][0];
while(son[u][1]) u=son[u][1];
return u;
}
int suc(int x)
{
find(x);
if(num[root]>x) return root;
int u=son[root][1];
while(son[u][0]) u=son[u][0];
return u;
}
void insert(int x)
{
int u=root,fu=0;
while(u&&x!=num[u])
{
fu=u;
u=son[u][x>num[u]];
}
if(num[u]==x) ++tot[u];
else
{
u=++cnt;
if(fu) son[fu][x>num[fu]]=u;
siz[u]=tot[u]=1;
fa[u]=fu,num[u]=x;
}
splay(u,0);
}
void remove(int x)
{
int lst=pre(x),nex=suc(x);
splay(lst,0);
if(nex!=lst)
splay(nex,lst);
int del=son[nex][0];
if(tot[del]>1)
--tot[del],splay(del,0);
else son[nex][0]=0;
}
int rk(int x)
{
find(x);
if(num[root]<x) return siz[root]-siz[son[root][1]];
return siz[son[root][0]];
}
int kth(int k)
{
int u=root;
while(1)
{
if(son[u][0]&&k<=siz[son[u][0]])
u=son[u][0];
else if(k>siz[son[u][0]]+tot[u])
k-=siz[son[u][0]]+tot[u],u=son[u][1];
else return num[u];
}
}
}sp[1000001];
int a[1000010];
int ans=0;
void build(int l,int r,int t)
{
for(int i=l;i<=r;++i)
sp[t].insert(a[i]);
if(l==r) return ;
int mid=(l+r)>>1;
build(l,mid,t<<1);
build(mid+1,r,t<<1|1);
}
int ll,rr;
int tree_rank(int l,int r,int t,int x)
{
if(ll<=l&&r<=rr)
return sp[t].rk(x)-1;
int mid=(l+r)>>1;
if(rr<=mid) return tree_rank(l,mid,t<<1,x);
if(ll>mid) return tree_rank(mid+1,r,t<<1|1,x);
return tree_rank(l,mid,t<<1,x)+tree_rank(mid+1,r,t<<1|1,x);
}
int tree_kth(int k)
{
int l=0,r=100000000;
while(l<=r)
{
int mid=(l+r)>>1;
int ans=tree_rank(1,n,1,mid)+1;
if(ans<=k) l=mid+1;
else r=mid-1;
}
return r;
}
void change(int l,int r,int t,int p,int pre,int nu)
{
sp[t].remove(pre),sp[t].insert(nu);
if(l==r) return ;
int mid=(l+r)>>1;
if(p<=mid)change(l,mid,t<<1,p,pre,nu);
else change(mid+1,r,t<<1|1,p,pre,nu);
}
int tree_pre(int l,int r,int t,int x)
{
if(ll<=l&&r<=rr)
return ans=max(ans,num[sp[t].pre(x)]);
int mid=(l+r)>>1;
if(ans==x-1) return ans;
if(rr<=mid) return ans=max(ans,tree_pre(l,mid,t<<1,x));
if(ll>mid) return ans=max(ans,tree_pre(mid+1,r,t<<1|1,x));
return ans=max(ans,max(tree_pre(l,mid,t<<1,x),tree_pre(mid+1,r,t<<1|1,x)));
}
int tree_suc(int l,int r,int t,int x)
{
if(ll<=l&&r<=rr)
return ans=min(num[sp[t].suc(x)],ans);
int mid=(l+r)>>1;
if(rr<=mid) return ans=min(ans,tree_suc(l,mid,t<<1,x));
if(ll>mid) return ans=min(ans,tree_suc(mid+1,r,t<<1|1,x));
return ans=min(min(tree_suc(l,mid,t<<1,x),tree_suc(mid+1,r,t<<1|1,x)),ans);
}
int main()
{
//freopen("2.in","r",stdin);
num[0]=-1;
n=read(),m=read();
for(int i=1;i<=n;++i)
a[i]=read();
/*for(int i=1;i<=n*20;++i)
sp[i].insert(-2147483647),sp[i].insert(2147483647);*/
build(1,n,1);
int opt,x,y,z;
for(int _=1;_<=m;++_)
{
opt=read();
switch(opt)
{
case 1:
ll=read(),rr=read(),z=read();
printf("%d\n",tree_rank(1,n,1,z)+1);break;
case 2:
ll=read(),rr=read(),z=read();
printf("%d\n",tree_kth(z));break;
case 3:
x=read(),y=read();
change(1,n,1,x,a[x],y),a[x]=y;break;
case 4:
ans=-2147483647;
ll=read(),rr=read(),z=read();
printf("%d\n",tree_pre(1,n,1,z));break;
case 5:
ans=2147483647;
ll=read(),rr=read(),z=read();
printf("%d\n",tree_suc(1,n,1,z));break;
}
}
return 0;
}
洛谷 P3380 【【模板】二逼平衡树(树套树)】的更多相关文章
- BZOJ3196 & 洛谷3380:二逼平衡树——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=3196 https://www.luogu.org/problemnew/show/P3380 (题 ...
- bzoj 3196 Tyvj 1730 二逼平衡树(线段树套名次树)
3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1807 Solved: 772[Submit][Stat ...
- bzoj 3196/ Tyvj 1730 二逼平衡树 (线段树套平衡树)
3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec Memory Limit: 128 MB[Submit][Status][Discuss] Description ...
- BZOJ3196 二逼平衡树 ZKW线段树套vector(滑稽)
我实在是不想再打一遍树状数组套替罪羊树了... 然后在普通平衡树瞎逛的时候找到了以前看过vector题解 于是我想:为啥不把平衡树换成vector呢??? 然后我又去学了一下ZKW线段树 就用ZKW线 ...
- BZOJ3196 二逼平衡树 【线段树套平衡树】
题目 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 1.查询k在区间内的排名 2.查询区间内排名为k的值 3.修改某一位值上的数值 4.查询k在区间内的前驱(前驱 ...
- BZOJ 3196 Tyvj 1730 二逼平衡树:线段树套splay
传送门 题意 给你一个长度为 $ n $ 有序数列 $ a $ ,进行 $ m $ 次操作,操作有如下几种: 查询 $ k $ 在区间 $ [l,r] $ 内的排名 查询区间 $ [l,r] $ 内排 ...
- 洛谷.3835.[模板]可持久化平衡树(fhq treap)
题目链接 对每次Merge(),Split()时产生的节点都复制一份(其实和主席树一样).时间空间复杂度都为O(qlogq).(应该更大些 因为rand()?内存真的爆炸..) 对于无修改的操作实际上 ...
- bzoj 3196 Tyvj 1730 二逼平衡树【线段树 套 splay】
四舍五入就是个暴力. 对于线段树的每个区间都开一棵按权值排序的splay 对于第二个操作,二分一下,每次查询mid的排名,复杂度 $ O(nlog(n)^{3}) $ 其余的操作都是$ O(nlog( ...
- [BZOJ3196] [Tyvj1730] 二逼平衡树(线段树 套 Splay)
传送门 至少BZOJ过了,其他的直接弃. 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 1.查询k在区间内的排名 2.查询区间内排名为k的值 3.修改某一位值上的 ...
- 洛谷P3380 【模板】二逼平衡树(树套树)(线段树+树状数组)
P3380 [模板]二逼平衡树(树套树) 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 查询k在区间内的排名 查询区间内排名为k的值 修改某一位值上的数 ...
随机推荐
- DBA思考系列——凛冬将至,丧钟为谁而鸣!
诸多迹象昭示着凛冬将至,大萧条终于正式在全国各地拉开了序幕,很多80后的国人没有经历过苦日子,也没有经历过真正的金融危机.这场经济危机必将摧毁一些无视经济能力,盲目购房,盲目消费的家庭或个人.个人对经 ...
- Python编写脚本(输出三星形状的‘*’符号)
环境:python3.* 心得:个人认为脚本非我强项,以下效果可以有更简单解决方案,纯属练习逻辑. 方案一: s=1 while s<=10: #这是决定多少列,起始为1,大循环一圈即加一,就是 ...
- spring boot 中使用 jpa以及jpa介绍
1.什么是jpa呢?JPA顾名思义就是Java Persistence API的意思,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中.12.jpa具有什么 ...
- mn
http://image.uczzd.cn/10129986679866437816.jpg?id=0&from=export https://www.cnblogs.com/ityoukno ...
- Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting for connection (Clien
https://blog.csdn.net/asahinokawa/article/details/84746422
- 8-过滤器Filter和监听器Listener
一.web监听器:监听特殊事件的发生1.监听实现步骤 a.写一个java类,实现特定的接口,重写相关方法 b.在web.xml中,牌配置 <listener> <listener-c ...
- Svn 安装、配置、使用指南
Svn 安装.配置.使用指南 Svn 是 Subversion 的简称,是一个开放源代码的版本控制系统,它采用了分支管理系统. 1. 安装配置 1.1. 安装 svn 1.2. 创建 svn 仓库 1 ...
- 不要再被骗了------QQ盗号原理大揭秘
前言 相信大家在懵懂无知的时候都有被盗号的经历吧,QQ胡乱的加好友,突然有个好友传了个文件给你,打开以后发现QQ竟然显示强制下线,然后再也上不去了QAQ,很明显,QQ号被人盗了.最近也是很多小伙伴私信 ...
- 判断语句之单if
什么是判断语句? 给定一个判断条件,并在程序执行过程中判断该条件是否成立,根据判断结果执行不同的操作,从而改变代码的执行顺序,实现更多的功能,这就是判断语句. 判断语句if if语句第一种格式:if ...
- 【转】shell脚本中如何传入参数
(1)直接用$1,$2取传入的参数vim /root/test.sh#!/bin/bashif [ $1 == "start" ] then echo "do ...