BZOJ_3196_二逼平衡树(树套树:线段树+Treap)

描述



可以处理区间问题的平衡树.

分析



树套树.可以用线段树套Treap.人生第一道树套树的题...

op1:如果在整区间,直接在该区间的treap上求解.否则分两个区间求解,然后相加.最后+1.

op2:这个不太好直接做,可以二分,每次假定一个值,用这个值去做op1,以此求得一个rank=k+1的数,求rank=k的数等价与求这个数的前驱pre.

op3:先删后加.

op4&op5:如果在整区间,直接在该区间的treap上求解,否则分量个区间求解,pre取最大值,suc取最小值.注意有些数在有些区间可能找不到前驱/后驱,这时pre返回-INF,suc取INF.

p.s.

1.神一样的调了一下午....上午做完普通平衡树感觉自己好不熟练啊居然搞了一上午,中午敲完这个题以为能A,结果RE,之后就没救了...发现时remove函数的问题,也没找到问题在哪,在开始怀疑人生之前换回了白书上的写法,果然A了,再看看原来的写法也没什么错啊.于是重新写了一遍原来的方法,结果A了.然后开始找不同,后来发现是结构体里定义的小于号不知咋地不好使了...C++学得太渣,最后只好舍弃,改为手动判断大小了...也不知道问题出在哪,最后还是没有解决,好遗憾...

1.白书上的remove写法

#include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std;

const int maxn=50000+5,oo=~0u>>1;
int n,q;
int a[maxn];
struct Treap{
    struct node{
        node* ch[2];
        int v,r,s,c;
        node(int v,node* t):v(v){ ch[0]=ch[1]=t; r=rand(); s=c=1; }
        bool operator < (const node &rhs) const{ return r<rhs.r; }
        void push_up() { s=ch[0]->s+ch[1]->s+c; }
    }*root,*null;
    Treap(){
        null=new node(0,NULL);
        null->c=null->s=0; null->r=oo;
        root=null;
    }
    void rotate(node* &o,bool d){
        node* k=o->ch[!d]; o->ch[!d]=k->ch[d]; k->ch[d]=o;
        o->push_up(); k->push_up(); o=k;
    }
    void insert(node* &o,int x){
        if(o==null) o=new node(x,null);
        else{
            if(x==o->v){ o->s++; o->c++; }
            else{
                bool d=x>o->v;
                insert(o->ch[d],x);
                if(o->ch[d]<o) rotate(o,!d);
                o->push_up();
            }
        }
    }
    void remove(node* &o,int x){
        if(o->v==x){
            if(o->c>1) { o->c--; o->s--; }
            else{
                if(o->ch[0]!=null&&o->ch[1]!=null){
                    bool d=o->ch[0]<o->ch[1];
                    rotate(o,d); remove(o->ch[d],x); o->push_up();
                }
                else{
                    node* u=o;
                    if(o->ch[0]==null) o=o->ch[1]; else o=o->ch[0];
                    delete u;
                }
            }
        }
        else{
            bool d=x>o->v;
            remove(o->ch[d],x);
            o->push_up();
        }
    }
    int rank(int x){
        int ret=0,s;
        for(node *t=root;t!=null;){
            s=t->ch[0]->s+t->c;
            if(x>t->v) ret+=s,t=t->ch[1];
            else t=t->ch[0];
        }
        return ret;
    }
    int pre(int x){
        int ret=-oo;
        for(node* t=root;t!=null;){
            if(t->v<x) ret=t->v,t=t->ch[1];
            else t=t->ch[0];
        }
        return ret;
    }
    int suc(int x){
        int ret=oo;
        for(node* t=root;t!=null;){
            if(t->v>x) ret=t->v,t=t->ch[0];
            else t=t->ch[1];
            }
        return ret;
    }
};

struct Segment_Tree{
    Treap tree[maxn*3];
    void build_tree(int l,int r,int k){
        for(int i=l;i<=r;i++) tree[k].insert(tree[k].root,a[i]);
        if(l==r) return;
        int mid=l+((r-l)>>1);
        build_tree(l,mid,k<<1); build_tree(mid+1,r,k<<1|1);
    }
    int get_rank(int l,int r,int k,int x,int y,int X){
        if(l==x&&r==y) return tree[k].rank(X);
        int mid=l+((r-l)>>1);
        if(y<=mid) return get_rank(l,mid,k<<1,x,y,X);
        else if(x>mid) return get_rank(mid+1,r,k<<1|1,x,y,X);
        else return get_rank(l,mid,k<<1,x,mid,X)+get_rank(mid+1,r,k<<1|1,mid+1,y,X);
    }
    void change(int l,int r,int k,int id,int x){
        tree[k].remove(tree[k].root,a[id]);
        tree[k].insert(tree[k].root,x);
        if(l==r) return;
        int mid=l+((r-l)>>1);
        if(id<=mid) change(l,mid,k<<1,id,x);
        else change(mid+1,r,k<<1|1,id,x);
    }
    int get_pre(int l,int r,int k,int x,int y,int X){
        if(l==x&&r==y) return tree[k].pre(X);
        int mid=l+((r-l)>>1);
        if(y<=mid) return get_pre(l,mid,k<<1,x,y,X);
        else if(x>mid) return get_pre(mid+1,r,k<<1|1,x,y,X);
        else return max(get_pre(l,mid,k<<1,x,mid,X),get_pre(mid+1,r,k<<1|1,mid+1,y,X));
    }
    int get_suc(int l,int r,int k,int x,int y,int X){
        if(l==x&&r==y) return tree[k].suc(X);
        int mid=l+((r-l)>>1);
        if(y<=mid) return get_suc(l,mid,k<<1,x,y,X);
        else if(x>mid) return get_suc(mid+1,r,k<<1|1,x,y,X);
        else return min(get_suc(l,mid,k<<1,x,mid,X),get_suc(mid+1,r,k<<1|1,mid+1,y,X));
    }
    int get_kth(int x,int y,int k){
        int l=0,r=oo;
        while(l<r){
            int mid=l+((r-l)>>1);
            int tmp=get_rank(1,n,1,x,y,mid)+1;
            if(tmp<=k) l=mid+1;
            else r=mid;
        }
        return get_pre(1,n,1,x,y,l);
    }
}T;

int main()
{
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    T.build_tree(1,n,1);
    int qry,l,r,k,x,pos;
    for(int i=1;i<=q;i++){
        scanf("%d",&qry);
        switch(qry){
            case 1:
                scanf("%d%d%d",&l,&r,&x);
                printf("%d\n",T.get_rank(1,n,1,l,r,x)+1);break;
            case 2:
                scanf("%d%d%d",&l,&r,&k);
                printf("%d\n",T.get_kth(l,r,k));break;
            case 3:
                scanf("%d%d",&pos,&x);
                T.change(1,n,1,pos,x);
                a[pos]=x;break;
            case 4:
                scanf("%d%d%d",&l,&r,&x);
                printf("%d\n",T.get_pre(1,n,1,l,r,x));break;
            case 5:
                scanf("%d%d%d",&l,&r,&x);
                printf("%d\n",T.get_suc(1,n,1,l,r,x));break;
        }
    }
    return 0;
}

2.另一种remove写法

#include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std;

const int maxn=50000+5,oo=~0u>>1;
int n,q;
int a[maxn];
struct Treap{
    struct node{
        node* ch[2];
        int v,r,s,c;
        node(int v,node* t):v(v){ ch[0]=ch[1]=t; r=rand(); s=c=1; }
        void push_up() { s=ch[0]->s+ch[1]->s+c; }
    }*root,*null;
    Treap(){
        null=new node(0,NULL);
        null->c=null->s=0; null->r=oo;
        root=null;
    }
    void rotate(node* &o,bool d){
        node* k=o->ch[!d]; o->ch[!d]=k->ch[d]; k->ch[d]=o;
        o->push_up(); k->push_up();
        if(o==root) root=k;
        o=k;
    }
    void insert(node* &o,int x){
        if(o==null) o=new node(x,null);
        else{
            if(x==o->v)  o->s++, o->c++;
            else{
                bool d=x>o->v;
                insert(o->ch[d],x);
                if(o->ch[d]<o) rotate(o,!d);
                o->push_up();
            }
        }
    }
    void remove(node* &o,int x) {
        if(x==o->v) {
            if(o->c>1) o->c--,o->s--;
            else{
                bool d=o->ch[1]->r<o->ch[0]->r;
                if(o->ch[d]==null){
                    delete o;
                    o=null;
                }
                else{
                    rotate(o,!d);
                    remove(o->ch[!d],x);
                    o->push_up();
                }
            }
        }
        else{
            bool d=x>o->v;
            remove(o->ch[d],x);
            o->push_up();
        }
    }
    int rank(int x){
        int ret=0,s;
        for(node *t=root;t!=null;){
            s=t->ch[0]->s+t->c;
            if(x>t->v) ret+=s,t=t->ch[1];
            else t=t->ch[0];
        }
        return ret;
    }
    int pre(int x){
        int ret=-oo;
        for(node* t=root;t!=null;){
            if(t->v<x) ret=t->v,t=t->ch[1];
            else t=t->ch[0];
        }
        return ret;
    }
    int suc(int x){
        int ret=oo;
        for(node* t=root;t!=null;){
            if(t->v>x) ret=t->v,t=t->ch[0];
            else t=t->ch[1];
            }
        return ret;
    }
};

struct Segment_Tree{
    Treap tree[maxn<<2];
    void build_tree(int l,int r,int k){
        for(int i=l;i<=r;i++) tree[k].insert(tree[k].root,a[i]);
        if(l==r) return;
        int mid=l+((r-l)>>1);
        build_tree(l,mid,k<<1); build_tree(mid+1,r,k<<1|1);
    }
    int get_rank(int l,int r,int k,int x,int y,int X){
        if(l==x&&r==y) return tree[k].rank(X);
        int mid=l+((r-l)>>1);
        if(y<=mid) return get_rank(l,mid,k<<1,x,y,X);
        else if(x>mid) return get_rank(mid+1,r,k<<1|1,x,y,X);
        else return get_rank(l,mid,k<<1,x,mid,X)+get_rank(mid+1,r,k<<1|1,mid+1,y,X);
    }
    void change(int l,int r,int k,int id,int x){
        tree[k].remove(tree[k].root,a[id]);
        tree[k].insert(tree[k].root,x);
        if(l==r) return;
        int mid=l+((r-l)>>1);
        if(id<=mid) change(l,mid,k<<1,id,x);
        else change(mid+1,r,k<<1|1,id,x);
    }
    int get_pre(int l,int r,int k,int x,int y,int X){
        if(l==x&&r==y) return tree[k].pre(X);
        int mid=l+((r-l)>>1);
        if(y<=mid) return get_pre(l,mid,k<<1,x,y,X);
        else if(x>mid) return get_pre(mid+1,r,k<<1|1,x,y,X);
        else return max(get_pre(l,mid,k<<1,x,mid,X),get_pre(mid+1,r,k<<1|1,mid+1,y,X));
    }
    int get_suc(int l,int r,int k,int x,int y,int X){
        if(l==x&&r==y) return tree[k].suc(X);
        int mid=l+((r-l)>>1);
        if(y<=mid) return get_suc(l,mid,k<<1,x,y,X);
        else if(x>mid) return get_suc(mid+1,r,k<<1|1,x,y,X);
        else return min(get_suc(l,mid,k<<1,x,mid,X),get_suc(mid+1,r,k<<1|1,mid+1,y,X));
    }
    int get_kth(int x,int y,int k){
        int l=0,r=oo;
        while(l<r){
            int mid=l+((r-l)>>1);
            int tmp=get_rank(1,n,1,x,y,mid)+1;
            if(tmp<=k) l=mid+1;
            else r=mid;
        }
        return get_pre(1,n,1,x,y,l);
    }
}T;

int main()
{
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    T.build_tree(1,n,1);
    int qry,l,r,k,x,pos;
    for(int i=1;i<=q;i++){
        scanf("%d",&qry);
        switch(qry){
            case 1:
                scanf("%d%d%d",&l,&r,&x);
                printf("%d\n",T.get_rank(1,n,1,l,r,x)+1);break;
            case 2:
                scanf("%d%d%d",&l,&r,&k);
                printf("%d\n",T.get_kth(l,r,k));break;
            case 3:
                scanf("%d%d",&pos,&x);
                T.change(1,n,1,pos,x);
                a[pos]=x;break;
            case 4:
                scanf("%d%d%d",&l,&r,&x);
                printf("%d\n",T.get_pre(1,n,1,l,r,x));break;
            case 5:
                scanf("%d%d%d",&l,&r,&x);
                printf("%d\n",T.get_suc(1,n,1,l,r,x));break;
        }
    }
    return 0;
}
时间: 2024-10-15 13:56:01

BZOJ_3196_二逼平衡树(树套树:线段树+Treap)的相关文章

BZOJ 3110 ZJOI 2013 K大数查询 树套树(权值线段树套区间线段树)

题目大意:有一些位置,这些位置上可以放若干个数字.现在有两种操作. 1.在区间l到r上添加一个数字x 2.求出l到r上的第k大的数字是什么 思路:这种题一看就是树套树,关键是怎么套,怎么写.(话说我也不会来着..)最容易想到的方法就是区间线段树套一个权值线段树,但是区间线段树上的标记就会变得异常复杂.所以我们就反过来套,用权值线段树套区间线段树.这样修改操作在外线段树上就变成了单点修改,外线段树就不用维护标记了.在里面的区间线段树上维护标记就容易多了.具体实现见代码. CODE: #includ

【BZOJ3110】【Zjoi2013】K大数查询 树套树 权值线段树套区间线段树

#include <stdio.h> int main() { puts("转载请注明出处谢谢"); puts("http://blog.csdn.net/vmurder/article/details/43020009"); } 题解: 外层权值线段树,内层区间线段树可解. 权值都是1~n,就不用离散化了. 我写了标记永久化. 其它心得神马的: 天生对树形数据结构无爱. 第一次写树套树,终于知道是怎么回事了. (只针对本题) 就是外层每个点都表示了一段

Codeforces 780G Andryusha and Nervous Barriers 线段树套set || 线段树套单调栈

Andryusha and Nervous Barriers 问题本质我们只需要找出每个线段两端下面第一个碰到的是哪个线段就好啦. 按照 h 排序我们就能用线段树套set 不管维护什么都能维护出这个东西,但是 如果set里维护 u + s的话,就能优化成单调栈, 好优秀啊. #include<bits/stdc++.h> #define LL long long #define LD long double #define ull unsigned long long #define fi f

【BZOJ3110】【codevs1616】K大数查询,权值线段树套普通线段树

Time:2016.05.09 Author:xiaoyimi 转载注明出处谢谢 传送门1 传送门2 思路: 之前没怎么接触过权值线段树(非主席树),这次就当学习了一下吧.一开始还把题意理解错了,我的天啊-- 起初思考了好久,发现不知道怎么处理负数的情况,不过数据里并没有负数? 权值线段树的每个节点表示一个区间[L,R],存储原序列中权值为[L,R]的元素的信息,所以这里的权值线段树每个节点上都是一棵普通线段树,就是负责统计原序列中权值为[L,R]的元素的个数. 每次插入一个相同的值x时就相当于

BZOJ3196 Tyvj1730 二逼平衡树

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3196 Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)5.查询k在区间内的后继(后继定义为大于x,且最小的数) Input 第一行两个数 n,m 表示长度为n的有序序列和m个操作第二行有n个数,表示有序

bzoj 3196/ Tyvj 1730 二逼平衡树 (线段树套平衡树)

3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)5.查询k在区间内的后继(后继定义为大于x,且最小的数) Input 第一行两个数 n,

bzoj 3196 &amp;&amp; luogu 3380 JoyOI 1730 二逼平衡树 (线段树套Treap)

链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3196 题面; 3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 6372  Solved: 2406[Submit][Status][Discuss] Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为

洛谷P3380 【模板】二逼平衡树(树套树,树状数组,线段树)

洛谷题目传送门 emm...题目名写了个平衡树,但是这道题的理论复杂度最优解应该还是树状数组套值域线段树吧. 就像dynamic ranking那样(蒟蒻的Sol,放一个link骗访问量233) 所有的值(包括初始a数组,操作1.3.4.5的k)全部先丢进去离散化 对于1操作查比它小的数,挑log棵线段树,找区间小于这个数的个数+1,这个还比较好像 操作2就是dynamic ranking,log棵线段树一起加减,像静态主席树求第k小一样跳,操作3 dynamic ranking里也有 操作4先

【bzoj3196】 Tyvj 1730 二逼平衡树 线段树套Treap

题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)5.查询k在区间内的后继(后继定义为大于x,且最小的数) 输入 第一行两个数 n,m 表示长度为n的有序序列和m个操作第二行有n个数,表示有序序列下面有m行,opt表示操作标号若opt=1 则为操作1,之后有三个数l,r,k 表示查询k在区间[l,r]的排名若opt=2 则为操