【模板】二逼平衡树(树套树)线段树套vector!!!

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

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

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

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

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

  5. 查询 \(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;
}

我爱VECTOR!!!!!!!!!!!!!!!!!

posted @ 2022-06-08 15:27  blue_tsg  阅读(63)  评论(2编辑  收藏  举报