【模板】二逼平衡树(树套树)线段树套vector!!!
题目描述
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:
-
查询 \(k\) 在区间内的排名
-
查询区间内排名为 \(k\) 的值
-
修改某一位值上的数值
-
查询 \(k\) 在区间内的前驱(前驱定义为严格小于 \(x\),且最大的数,若不存在输出
-2147483647
) -
查询 \(k\) 在区间内的后继(后继定义为严格大于 \(x\),且最小的数,若不存在输出
2147483647
)
输入格式
第一行两个数 \(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
样例输入 #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
分析
很明显这题是树套树
非常经典的一个做法是线段树套平衡树,Treap,Splay啥的。。。
但是我这么懒怎么会打平衡树呢
所以我来提供一个,及 其 暴 力 又 方 便 的 做 法!
众所周知vector是可以模拟平衡树的
那么我们为什么不让线段树套vector呢?
网上关于这种做法的题解很少,有的也用了很多指针等奇怪操作。。。
所以本蒟蒻出于对vector极大的热爱,怀着试一试的心态打了打线段树套vector
结果,什么优化都没有就A掉了(除了O2)
当然效率肯定没有平衡树高,但是好写又好调呀:)
当然没有O2就比较凄惨了
好了不多废话了来看看到底怎么打
解决
vector模拟平衡树的基础操作看这里--->如何优雅的用vector切掉平衡树?? - blue_tsg - 博客园 (cnblogs.com)
好了其实很简单了已经,我们把线段树的每个节点都存上一个vector,表示当前这个区间的平衡树
对于各种操作
1.排名:如同正常线段树一样找到各个区间,求出在每个区间内小于x的数的个数,递归合并时全部加起来,最后输出时要加1
2.第k大,因为这个没有区间可加性,我们可以二分答案,判断x是不是区间第k大,复杂度lognlognlogn
注意一点二分区间,一定要把值域区间全部含进去,比如0
3.修改,就是删除原数,再加上新数,注意线段树递归时要每到一个节点都改了
4.前驱后继,同样的,找出每个区间的x前驱,然后取最小的,后继就是取最大的
这里注意如果没有前驱和后继vector可能会炸掉,同时题目中也说了要输出无穷,所以要特判一下(具体见代码)
然后就愉快的AC啦
Talk is cheap,Show me the Code
#include <cstdio>
#include <cstring>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <vector>
#define endl putchar('\n')
#define lson(x) (x<<1)
#define rson(x) (x<<1|1)
#define lb lower_bound
#define ub upper_bound
using namespace std;
const int M = 1e6+10;
const int inf = 2147483647;
typedef long long ll;
inline int read(){
int x=0,f=0;char c=getchar();
while(!isdigit(c)){
if(c=='-') f=1;c=getchar();
}
do{
x=(x<<1)+(x<<3)+(c^48);
}while(isdigit(c=getchar()));
return f?-x:x;
}
inline void print(int x){
if(x<0) putchar('-'),x=-x;
if(x>9) print(x/10);putchar(x%10^48);
}
struct SegmentTree{
vector<int> vec;//一颗平衡树
int l,r;
SegmentTree(){
l=0;r=0;
}
int Rank(int x){//查排名,实际上是查比x小的数有多少
return lb(vec.begin(),vec.end(),x)-vec.begin();
}
void Insert(int x){//插入
vec.insert(lb(vec.begin(),vec.end(),x),x);
}
void update(int x,int k){//权值为x变为k
vec.erase(lb(vec.begin(),vec.end(),x));
vec.insert(lb(vec.begin(),vec.end(),k),k);
}
int Pre(int x){//前驱
int f=lb(vec.begin(),vec.end(),x)-vec.begin();
if(f==0) return -inf;//特判
return vec[f-1];
}
int Next(int x){//后继
int f=ub(vec.begin(),vec.end(),x)-vec.begin();
if(f==vec.size()) return inf;//特判
return vec[f];
}
void debug(){//贴心的调试函数,可以输出当前节点的存储情况
printf("l=%d r=%d:",l,r);
for(int i=0;i<vec.size();i++) printf("%d ",vec[i]);
printf("\n");
}
};
SegmentTree tree[M];int tot;
int a[M];
int n,m;
void build(int rt,int l,int r){
tree[rt].l=l;tree[rt].r=r;
for(int i=l;i<=r;i++) tree[rt].Insert(a[i]);//建树时全部插进去
if(l==r) return;
int mid=(l+r)>>1;
build(lson(rt),l,mid);
build(rson(rt),mid+1,r);
}
int Query_Rank(int rt,int l,int r,int L,int R,int x){
int ans=0;
if(l<=L&&R<=r){
return tree[rt].Rank(x);
}
int mid=(L+R)>>1;
if(l<=mid) ans+=Query_Rank(lson(rt),l,r,L,mid,x);
if(r>mid) ans+=Query_Rank(rson(rt),l,r,mid+1,R,x);
return ans;
}
int Query_kth(int l,int r,int k){
int L=0,R=1e8,ans=-1;
while(L<=R){
int mid=(L+R)>>1;
if(Query_Rank(1,l,r,1,n,mid)+1<=k) ans=mid,L=mid+1;
else R=mid-1;
}
return ans;
}
void Update(int rt,int l,int r,int pos,int x){
if(l==r) return;
int mid=(l+r)>>1;
if(pos<=mid) Update(lson(rt),l,mid,pos,x);
else Update(rson(rt),mid+1,r,pos,x);
}
int up(int rt,int l,int r,int L,int R,int x){
int mid=(L+R)>>1;
if(l<=L&&R<=r) return tree[rt].Pre(x);
int ans1=-inf,ans2=-inf;
if(l<=mid) ans1=up(lson(rt),l,r,L,mid,x);
if(r>mid) ans2=up(rson(rt),l,r,mid+1,R,x);
return max(ans1,ans2);
}
int down(int rt,int l,int r,int L,int R,int x){
int mid=(L+R)>>1;
if(l<=L&&R<=r) return tree[rt].Next(x);
int ans1=inf,ans2=inf;
if(l<=mid) ans1=down(lson(rt),l,r,L,mid,x);
if(r>mid) ans2=down(rson(rt),l,r,mid+1,R,x);
return min(ans1,ans2);
}
int main(){
n=read();m=read();
for(int i=1;i<=n;i++) a[i]=read();
build(1,1,n);//别忘了建树
while(m--){
int op=read(),l,r,pos,x;
if(op==1){
l=read();r=read();x=read();
printf("%d\n",Query_Rank(1,l,r,1,n,x)+1);
}
if(op==2){
l=read();r=read();x=read();
printf("%d\n",Query_kth(l,r,x));
}
if(op==3){
pos=read();x=read();
Update(1,1,n,pos,x);
a[pos]=x;
}
if(op==4){
l=read();r=read();x=read();
printf("%d\n",up(1,l,r,1,n,x));
}
if(op==5){
l=read();r=read();x=read();
printf("%d\n",down(1,l,r,1,n,x));
}
}
return 0;
}
当然还有压行版的:
#include <bits/stdc++.h>
#define lson(x) (x<<1)
#define rson(x) (x<<1|1)
using namespace std;
const int M = 1e6+10;
const int inf = 2147483647;
typedef long long ll;
inline int read(){
int x=0,f=0;char c=getchar();
while(!isdigit(c)){
if(c=='-') f=1;c=getchar();
}
do{
x=(x<<1)+(x<<3)+(c^48);
}while(isdigit(c=getchar()));
return f?-x:x;
}
inline void print(int x){
if(x<0) putchar('-'),x=-x;
if(x>9) print(x/10);putchar(x%10^48);
}
struct SegmentTree{
vector<int> vec;int l,r;
SegmentTree(){}
int Rank(int x){return lower_bound(vec.begin(),vec.end(),x)-vec.begin();}
void Insert(int x){vec.insert(lower_bound(vec.begin(),vec.end(),x),x);}
void update(int x,int k){vec.erase(lower_bound(vec.begin(),vec.end(),x));vec.insert(lower_bound(vec.begin(),vec.end(),k),k);}
int Pre(int x){return !(lower_bound(vec.begin(),vec.end(),x)-vec.begin())?-inf:vec[lower_bound(vec.begin(),vec.end(),x)-vec.begin()-1];}
int Next(int x){return (upper_bound(vec.begin(),vec.end(),x)-vec.begin()==vec.size())?inf:vec[upper_bound(vec.begin(),vec.end(),x)-vec.begin()];}
};
SegmentTree tree[M];int tot;
int a[M],n,m;
void build(int rt,int l,int r){
tree[rt].l=l;tree[rt].r=r;
for(int i=l;i<=r;i++) tree[rt].Insert(a[i]);
if(l==r) return;
int mid=(l+r)>>1;
build(lson(rt),l,mid);
build(rson(rt),mid+1,r);
}
int Query_Rank(int rt,int l,int r,int L,int R,int x){
int ans=0;
if(l<=L&&R<=r) return tree[rt].Rank(x);
int mid=(L+R)>>1;
if(l<=mid) ans+=Query_Rank(lson(rt),l,r,L,mid,x);
if(r>mid) ans+=Query_Rank(rson(rt),l,r,mid+1,R,x);
return ans;
}
int Query_kth(int l,int r,int k){
int L=0,R=1e8,ans=0;
while(L<=R){
int mid=(L+R)>>1;
if(Query_Rank(1,l,r,1,n,mid)+1<=k) ans=mid,L=mid+1;
else R=mid-1;
}
return ans;
}
void Update(int rt,int l,int r,int pos,int x){
tree[rt].update(a[pos],x);
if(l==r) return;
int mid=(l+r)>>1;
(pos<=mid)?Update(lson(rt),l,mid,pos,x):Update(rson(rt),mid+1,r,pos,x);
}
int up(int rt,int l,int r,int L,int R,int x){
int mid=(L+R)>>1;
if(l<=L&&R<=r) return tree[rt].Pre(x);
int ans1=-inf,ans2=-inf;
if(l<=mid) ans1=up(lson(rt),l,r,L,mid,x);
if(r>mid) ans2=up(rson(rt),l,r,mid+1,R,x);
return max(ans1,ans2);
}
int down(int rt,int l,int r,int L,int R,int x){
int mid=(L+R)>>1;
if(l<=L&&R<=r) return tree[rt].Next(x);
int ans1=inf,ans2=inf;
if(l<=mid) ans1=down(lson(rt),l,r,L,mid,x);
if(r>mid) ans2=down(rson(rt),l,r,mid+1,R,x);
return min(ans1,ans2);
}
signed main(){
n=read();m=read();
for(int i=1;i<=n;i++) a[i]=read();
build(1,1,n);
while(m--){
int op=read(),l=read(),r=read(),pos,x;
switch(op){
case 1:x=read();printf("%d\n",Query_Rank(1,l,r,1,n,x)+1);break;
case 2:x=read();printf("%d\n",Query_kth(l,r,x));;break;
case 3:Update(1,1,n,l,r);a[l]=r;break;
case 4:x=read();printf("%d\n",up(1,l,r,1,n,x));break;
case 5:x=read();printf("%d\n",down(1,l,r,1,n,x));break;
}
}
return 0;
}