线段树套无旋streap树(模板)

题目链接:https://www.luogu.org/problemnew/show/P3380

感谢:https://blog.csdn.net/chenxiaoran666/article/details/82949397 

一个磕了几天的模板题,一个rand()的打错就T到疯。MMP。

自己写的一个板子。

#include<algorithm>
#include<string.h>
#include<stdio.h>
using namespace std;
const int Max=1e4*5+10;
struct node
{
    int child[2],s,v,key;
} A[30*Max];
int tot=0,a[Max],see=233;
int n,m;
inline int read()//快读
{
    char ch = getchar();
    int x = 0, f = 1;
    while(ch < '0' || ch > '9')
    {
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while('0' <= ch && ch <= '9')
    {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}
class treap//无旋streap树
{
private:
#define rand() ((int)(see*=482711ll % 2147483647))//我TM之前忘记打了=号,T了一天,找了一天的bug
#define ls(x) A[x].child[0]
#define rs(x) A[x].child[1]
    void up(int r)
    {
        A[r].s=A[ls(r)].s+A[rs(r)].s+1;
        return ;
    }
public:
    int root=0;
    void Split(int r,int &x,int &y,int k)
    {
        if(!r)
        {
            x=y=0;
            return ;
        }
        if(A[r].v<=k) x=r,Split(rs(r),rs(x),y,k);
        else y=r,Split(ls(r),x,ls(y),k);
        up(r);
        return ;
    }
    void Merge(int &r,int x,int y)
    {
        if(!x||!y)
        {
            r=x+y;
            return ;
        }
        if(A[x].key<A[y].key) r=x,Merge(rs(r),rs(x),y);
        else r=y,Merge(ls(r),x,ls(y));
        up(r);
        return ;
    }
    int Insert(int& r,int k)
    {
        int x=0,y=0,z=++tot;
        ls(z)=rs(z)=0,A[z].v=k,A[z].s=1,A[z].key=rand();
        Split(r,x,y,k);
        Merge(x,x,z);
        Merge(r,x,y);
        return 0;
    }
    int del(int &r,int k)
    {
        int x=0,y=0,z=0;
        Split(r,x,y,k);
        Split(x,x,z,k-1);
        Merge(z,ls(z),rs(z));
        Merge(x,x,z);
        Merge(r,x,y);
        return 0;
    }
    int Rank(int r,int k)//这里需要改变一下,ans不用+1。
    {
        int ans=0;
        while(r)
        {
            if(A[r].v>=k) r=ls(r);
            else ans+=A[ls(r)].s+1,r=rs(r);
        }
        return ans;
    }
    int kth(int r,int k)
    {
        if(A[r].s<k) return -1;
        while(A[ls(r)].s+1!=k)
        {
            if(A[ls(r)].s+1>k)r=ls(r);
            else k-=A[ls(r)].s+1,r=rs(r);
        }
        return A[r].v;
    }
    int pre(int &r,int k)
    {
        int x=0,y=0,ans;
        Split(r,x,y,k-1);
        ans=x?kth(x,A[x].s):-2147483647;
        Merge(r,x,y);
        return ans;
    }
    int sub(int &r,int k)
    {
        int x=0,y=0,ans;
        Split(r,x,y,k);
        ans=y?kth(y,1):2147483647;
        Merge(r,x,y);
        return ans;
    }
};
class segment//线段树
{
private:
#define rt T[k].root
    treap T[4*Max];
public:
    void Build(int l,int r,int k)//建树,每个区间都是一颗平衡树
    {
        for(int i=l; i<=r; i++) T[k].Insert(rt,a[i]);
        int mid=(l+r)>>1;
        if(r>l) Build(l,mid,2*k),Build(mid+1,r,2*k+1);
        return ;
    }
    void update(int l,int r,int k,int pos,int x)//每个区间都更新,先把值删去,在加入新的值
    {
        if(l<=pos&&pos<=r) T[k].del(rt,a[pos]),T[k].Insert(rt,x);
        int mid=(l+r)>>1;
        if(r>l) pos>mid?update(mid+1,r,2*k+1,pos,x):update(l,mid,2*k,pos,x);
        return ;
    }
    int _rank(int l,int r,int k,int ql,int qr,int x)//找出每个区间小于x的数的和
    {
        int ans=0;
        int mid=(l+r)>>1;
        if(ql<=l&&r<=qr) return T[k].Rank(rt,x);
        if(ql<=mid) ans+=_rank(l,mid,2*k,ql,qr,x);
        if(qr>mid) ans+=_rank(mid+1,r,2*k+1,ql,qr,x);
        return ans;
    }
    int _kth(int ql,int qr,int k)//二分,看看那个值符合第k大,“最大”的符合值就是答案
    {
        int l=0,r=1e8;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(_rank(1,n,1,ql,qr,mid)+1<=k) l=mid+1;
            else r=mid-1;
        }
        return r;
    }
    int _pre(int l,int r,int k,int ql,int qr,int x)//求所有离散区间前驱,取最大值
    {
        int ans=-2147483647;
        int mid=(l+r)>>1;
        if(ql<=l&&r<=qr)
        {
            return T[k].pre(rt,x);
        }
        if(ql<=mid) ans=max(ans,_pre(l,mid,2*k,ql,qr,x));
        if(qr>mid) ans=max(ans,_pre(mid+1,r,2*k+1,ql,qr,x));
        return ans;
    }
    int _sub(int l,int r,int k,int ql,int qr,int x)//求所有离散区间的后继,取最小值
    {
        int ans=2147483647;
        int mid=(l+r)>>1;
        if(ql<=l&&r<=qr) return T[k].sub(rt,x);
        if(ql<=mid) ans=min(ans,_sub(l,mid,2*k,ql,qr,x));
        if(qr>mid) ans=min(ans,_sub(mid+1,r,2*k+1,ql,qr,x));
        return ans;
    }
};
int main()
{
//    freopen("1.txt","r",stdin);
//    freopen("3.txt","w",stdout);
    int opt,x,y,z;
    n=read(),m=read();
    for(int i=1; i<=n; i++)
        a[i]=read();
    segment mary;
    mary.Build(1,n,1);
    while(m--)
    {
        opt=read();
        if(opt==1)//查询k在区间内的排名
        {
            x=read(),y=read(),z=read();
            printf("%d\n",mary._rank(1,n,1,x,y,z)+1);
        }
        if(opt==2)//查询区间内排名为k的值
        {
            x=read(),y=read(),z=read();
            printf("%d\n",mary._kth(x,y,z));
        }
        if(opt==3)//修改某一位值上的数值
        {
            x=read(),z=read();
            mary.update(1,n,1,x,z);
            a[x]=z;
        }
        if(opt==4)//查询k在区间内的前驱(前驱定义为严格小于x,且最大的数,若不存在输出-2147483647)
        {
            x=read(),y=read(),z=read();
            printf("%d\n",mary._pre(1,n,1,x,y,z));
        }
        if(opt==5)//查询k在区间内的后继(后继定义为严格大于x,且最小的数,若不存在输出2147483647)
        {
            x=read(),y=read(),z=read();
            printf("%d\n",mary._sub(1,n,1,x,y,z));
        }
    }
    return 0;
}
/*
输入格式:
第一行两个数 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各输出一行,表示查询结果
*/

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值