BZOJ3196 Tyvj1730 二逼平衡树


Description

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

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

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

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

4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)

5.查询k在区间内的后继(后继定义为大于x,且最小的数)

Input

第一行两个数 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的后继

Output

对于操作1,2,4,5各输出一行,表示查询结果

Sample Input

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

Sample Output

2

4

3

4

9

HINT

1.n和m的数据范围:n,m<=50000

2.序列中每个数的数据范围:[0,1e8]

3.虽然原题没有,但事实上5操作的k可能为负数


前置知识:treap基本操作+线段树区间维护思想

第一次写线段树套treap题,僵硬了两天,最后在网上找标程用数据拍过了。。

其实思路挺简单的,对于每个区间[l,r]都维护一棵平衡树不想写splay,用线段树维护l和r的信息再一起来更新,算是板子题了吧。。。结果我把siz数组的更新写挂了,另外要注意的是需要维护有重复点值的情况,对于一个新手来说细节还是蛮多的,话不多说上代码


#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 200010
#define M 3000010
#define LD (t<<1)
#define RD (t<<1|1)
int n,m,tmp,tot=0,a[N],root[N];
int ls[M],rs[M],key[M],val[M],siz[M],w[M];
//旋转和更新
void update(int t){siz[t]=siz[ls[t]]+siz[rs[t]]+w[t];}
int lturn(int t){int k=rs[t];rs[t]=ls[k];ls[k]=t;update(t);update(k);return k;}
int rturn(int t){int k=ls[t];ls[t]=rs[k];rs[k]=t;update(t);update(k);return k;}
//构造节点
void new_treap_point(int &t,int vl){
t=++tot;
ls[t]=rs[t]=0;
siz[t]=w[t]=1;
key[t]=rand();
val[t]=vl;
}
//插入新节点
void insert(int &t,int vl){
//没有节点 新建节点
if(!t){new_treap_point(t,vl);return;}
//找到节点 点值加1
siz[t]++;
if(val[t]==vl){w[t]++;return;}
//递归左右子树
if(vl<val[t]){
insert(ls[t],vl);
if(key[ls[t]]<key[t])t=rturn(t);
}else{
insert(rs[t],vl);
if(key[rs[t]]<key[t])t=lturn(t);
}
}
//删除节点
void Delete(int &t,int vl){
if(!t)return;
//向左右儿子递归问题
if(vl<val[t]&&ls[t]){Delete(ls[t],vl);update(t);return;}
if(vl>val[t]&&rs[t]){Delete(rs[t],vl);update(t);return;}
//节点不存在
if(vl!=val[t])return;
//找到 处理问题
//当前节点个数大于一 直接删除一个
if(w[t]>1){w[t]--;update(t);return;}
//没有左右儿子 直接删除改节点
if(!ls[t]&&!rs[t]){t=0;return;}
//左右儿子有一个不存在 将当前节点覆盖
if(!ls[t]||!rs[t]){t=ls[t]+rs[t],update(t);return;}
//比较左右儿子的key值 翻转并递归问题
if(key[ls[t]]<key[rs[t]])t=rturn(t),Delete(rs[t],vl);
if(key[ls[t]]>key[rs[t]])t=lturn(t),Delete(ls[t],vl);
update(t);
}
//求节点值的排名
void get_rank(int t,int vl){
if(!t)return;
//找到 加上比他小的数字个数
if(vl==val[t]){tmp+=siz[ls[t]];return;}
//递归左子树
if(vl<val[t])get_rank(ls[t],vl);
//递归右子树 排名加上左子树和其本身的排名值
else tmp+=siz[ls[t]]+w[t],get_rank(rs[t],vl);
}
//求前驱
void get_pre(int t,int vl){
if(!t)return;
if(val[t]<vl){
//当前值小于查询值 更新答案 递归右子树
tmp=max(tmp,val[t]);
get_pre(rs[t],vl);
}else get_pre(ls[t],vl);
}
//求后继
void get_nxt(int t,int vl){
if(!t)return;
if(val[t]>vl){
//当前值大于查询值 更新答案 递归左子树
tmp=min(tmp,val[t]);
get_nxt(ls[t],vl);
}else get_nxt(rs[t],vl);
}
//建外层树
void build(int t,int l,int r,int pos,int vl){
insert(root[t],vl);
if(l==r)return;
int mid=(l+r)>>1;
//递归建立子树
if(pos<=mid)build(LD,l,mid,pos,vl);
else build(RD,mid+1,r,pos,vl);
}
//线段树上查询rank
void query_rank(int t,int l,int r,int x,int y,int vl){
if(l==x&&r==y){get_rank(root[t],vl);return;}
int mid=(l+r)>>1;
//判断区间和mid的关系
if(mid>=y){query_rank(LD,l,mid,x,y,vl);return;}
if(mid+1<=x){query_rank(RD,mid+1,r,x,y,vl);return;}
query_rank(LD,l,mid,x,mid,vl);
query_rank(RD,mid+1,r,mid+1,y,vl);
}
//查询第k大的数
int query_kth(int x,int y,int k){
int l=0,r=INF,ans;
while(l<=r){
int mid=(l+r)>>1;
tmp=1;query_rank(1,1,n,x,y,mid);
if(tmp<=k)l=mid+1,ans=mid;
else r=mid-1;
}
return ans;
}
//修改点值
void modify(int t,int l,int r,int pos,int vl,int vl_old){
//删除旧点值 插入新点值 等价于改变点值
Delete(root[t],vl_old);
insert(root[t],vl);
if(l==r)return;
int mid=(l+r)>>1;
if(pos<=mid)modify(LD,l,mid,pos,vl,vl_old);
if(pos>mid)modify(RD,mid+1,r,pos,vl,vl_old);
}
//线段树查询前驱
void query_pre(int t,int l,int r,int x,int y,int vl){
if(l==x&&r==y){get_pre(root[t],vl);return;}
int mid=(l+r)>>1;
if(y<=mid){query_pre(LD,l,mid,x,y,vl);return;}
if(mid+1<=x){query_pre(RD,mid+1,r,x,y,vl);return;}
query_pre(LD,l,mid,x,mid,vl);
query_pre(RD,mid+1,r,mid+1,y,vl);
}
//线段树查询后继
void query_nxt(int t,int l,int r,int x,int y,int vl){
if(l==x&&r==y){get_nxt(root[t],vl);return;}
int mid=(l+r)>>1;
if(y<=mid){query_nxt(LD,l,mid,x,y,vl);return;}
if(mid+1<=x){query_nxt(RD,mid+1,r,x,y,vl);return;}
query_nxt(LD,l,mid,x,mid,vl);
query_nxt(RD,mid+1,r,mid+1,y,vl);
}
//输出检查
void put_out(int t){
if(ls[t])put_out(ls[t]);
cout<<t<<" "<<siz[t]<<" "<<w[t]<<" "<<val[t]<<endl;
if(rs[t])put_out(rs[t]);
}
int main(){
//freopen("bzoj3196.in","r",stdin);
//freopen("bzoj3196.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),build(1,1,n,i,a[i]);
for(int i=1;i<=m;i++){
int op,x,y,k;scanf("%d",&op);
if(op==1){
scanf("%d%d%d",&x,&y,&k);
tmp=1;
query_rank(1,1,n,x,y,k);
printf("%d\n",tmp);
}else if(op==2){
scanf("%d%d%d",&x,&y,&k);
printf("%d\n",query_kth(x,y,k));
}else if(op==3){
scanf("%d%d",&x,&y);
modify(1,1,n,x,y,a[x]);
a[x]=y;
}else if(op==4){
scanf("%d%d%d",&x,&y,&k);
tmp=-INF;
query_pre(1,1,n,x,y,k);
printf("%d\n",tmp);
}else if(op==5){
scanf("%d%d%d",&x,&y,&k);
tmp=INF;
query_nxt(1,1,n,x,y,k);
printf("%d\n",tmp);
}
}
return 0;
}

BZOJ3196 Tyvj1730 二逼平衡树 【树套树】 【线段树套treap】的更多相关文章

  1. [BZOJ3196][Tyvj1730]二逼平衡树

    [BZOJ3196][Tyvj1730]二逼平衡树 试题描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 查询 \(k\) 在区间内的排名 查询区间内排名为 \ ...

  2. [bzoj3196][Tyvj1730]二逼平衡树_树套树_位置线段树套非旋转Treap/树状数组套主席树/权值线段树套位置线段树

    二逼平衡树 bzoj-3196 Tyvj-1730 题目大意:请写出一个维护序列的数据结构支持:查询给定权值排名:查询区间k小值:单点修改:查询区间内定值前驱:查询区间内定值后继. 注释:$1\le ...

  3. [BZOJ3196] [Tyvj1730] 二逼平衡树(线段树 套 Splay)

    传送门 至少BZOJ过了,其他的直接弃. 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 1.查询k在区间内的排名 2.查询区间内排名为k的值 3.修改某一位值上的 ...

  4. bzoj3196:Tyvj1730二逼平衡树

    传送门 暴力啊,直接树套树上啊 线段树套splay,卡卡常就直接A了 代码: #include<cstdio> #include<iostream> #include<a ...

  5. bzoj3196 [TYVJ1730]二逼平衡树 树套树 线段树套替罪羊树

    人傻自带大常数 二分的可行性证明: 贴近他的正确答案不会被当作次优解删掉,因为,若二分在他右边发生,那么二分一定会把左边作为优解,左边同理,所以他一定是被扣掉的所以最后一个小于等于一定是正确答案 #i ...

  6. 【BZOJ3196】二逼平衡树(树状数组,线段树)

    [BZOJ3196]二逼平衡树(树状数组,线段树) 题面 BZOJ题面 题解 如果不存在区间修改操作: 搞一个权值线段树 区间第K大--->直接在线段树上二分 某个数第几大--->查询一下 ...

  7. [TYVJ1730]二逼平衡树

    [TYVJ1730]二逼平衡树 题目 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查 ...

  8. 【BZOJ-3196】二逼平衡树 线段树 + Splay (线段树套平衡树)

    3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2271  Solved: 935[Submit][Stat ...

  9. 【bzoj3196】 Tyvj1730—二逼平衡树

    http://www.lydsy.com/JudgeOnline/problem.php?id=3196 (题目链接) 题意 1.查询k在区间内的排名:2.查询区间内排名为k的值:3.修改某一位值上的 ...

随机推荐

  1. HDU 3639 Hawk-and-Chicken(强连通缩点+反向建图)

    http://acm.hdu.edu.cn/showproblem.php?pid=3639 题意: 有一群孩子正在玩老鹰抓小鸡,由于想当老鹰的人不少,孩子们通过投票的方式产生,但是投票有这么一条规则 ...

  2. POJ 1144 Network(无向图的割顶和桥模板题)

    http://poj.org/problem?id=1144 题意: 给出图,求割点数. 思路: 关于无向图的割顶和桥,这篇博客写的挺不错,有不懂的可以去看一下http://blog.csdn.net ...

  3. 解决 对路径bin\roslyn..的访问被拒绝

    使用visual studio开发,一重新编译就会报错: 对路径“bin\roslyn\System.Reflection.Metadata.dll”的访问被拒绝 一开始的解决办法就是把bin下的文件 ...

  4. form表单提交数据的数据格式

    form表单提交的数据格式默认是 enctype="application/x-www-form-urlencoded"这样将input框的数据与input框的name属性以键值对 ...

  5. 原生javascript-无间缝滚动,封装

    目前支持的是竖向与横向滚动 http://lgy.1zwq.com/marScroll/ 现在分析下无间缝实现的基本思路(竖向例子): HTML结构: <div id="marScro ...

  6. Hadoop出现 Wrong FS: hdfs://......错误的解决方法

    今天在hadoop项目中出现以下报错:java.lang.IllegalArgumentException: Wrong FS: hdfs://......,expected: file:///... ...

  7. 利用Docker编译Hadoop 3.1.0

    前言 为什么要使用Docker编译,请自行百度 操作系统环境:Centos 6.8 uname -r 内核版本:2.6.32-642.el6.x86_64 除非有把握否则不要在Centos6.8中直接 ...

  8. cx_freeze打包EXE文件

    创建setup.py文件 import osimport sysfrom cx_Freeze import setup, Executable build_exe_options = dict(pac ...

  9. canvas 遮罩

    上一篇介绍了CSS3可以实现mask的方式,本篇介绍canvas同样也可以实现遮罩的方法: 原理: canvas是在画布上绘图,可以绘制各种形状,同时可以在一个层上重复画图,默认情况下后面的会覆盖前面 ...

  10. python之阶乘的小例子

    现在自己写阶乘是这个样子的 def f(x): return x * f(x-1) if x >1 else 1 后来无意中看到耗子的一篇<Python程序员的进化>的文章, 感脚这 ...