zoukankan      html  css  js  c++  java
  • BZOJ3196 Tyvj1730 二逼平衡树 【树套树】 【线段树套treap】

    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
    ",tmp);
            }else if(op==2){
                scanf("%d%d%d",&x,&y,&k);
                printf("%d
    ",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
    ",tmp);
            }else if(op==5){
                scanf("%d%d%d",&x,&y,&k);
                tmp=INF;
                query_nxt(1,1,n,x,y,k);
                printf("%d
    ",tmp);
            }
        }
        return 0;
    }
  • 相关阅读:
    Delphi中Indy10的IdTcpClient和IdTcpServer数据通信
    Delphi中TStringList的使用
    VC2008中处理CStatic控件的单击STN_CLICKED消息
    VS2008中编译运行MFC应用程序时,出现无法启动程序,因为计算机中丢失mfc90ud.dll的解决方案
    adb 常用命令大全
    Windows XP 下如何使用Qt Creator中的Git版本控制功能
    git安装及命令使用和github网站
    Win7下VS2008安装cocos2d-2.0-x-2.0.4模板时, 运行InstallWizardForVS2008.js文件执行失败的解决办法
    VC2008中将CString转换成const char*的一种有效方法
    VS2008中MFC对话框界面编程Caption中文乱码的解决办法
  • 原文地址:https://www.cnblogs.com/dream-maker-yk/p/9676404.html
Copyright © 2011-2022 走看看