BZOJ 3196 二逼平衡树 线段树+treap

题意链接

方法:线段树+treap的模板题

题解

首先是对于整个树的定义,其实treap部分并没有什么区别,只不过是单root改变为多root而已。

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define INF 0x3f3f3f3f
#define N 200100
#define M 3000100
#define K 50100
using namespace std ;
//define
struct node
{
    int l,r,w,v,size,rnd;
}tr[M] ;
int n , m , size , ans;
int a[K] ;
int root[N] ;

然而这是treap的建图部分,build部分其实是线段树内的,原来的build内读值改变为insert即可。del操作其实就是把原来的点删掉,再加新的点即可。

//  treap
void make_new(int k)
{
    tr[k].size = tr[tr[k].l].size + tr[tr[k].r].size + tr[k].w ;
}
void lturn(int &k)
{
    int t = tr[k].r ;
    tr[k].r = tr[t].l ;
    tr[t].l = k ;
    tr[t].size = tr[k].size ;
    make_new(k) ;
    k = t ;
}
void rturn(int &k)
{
    int t = tr[k].l ;
    tr[k].l = tr[t].r ;
    tr[t].r = k ;
    tr[t].size = tr[k].size ;
    make_new(k) ;
    k = t ;
}
void insert(int &k , int x)
{
    if(!k)
    {
        k = ++size ;
        tr[k].size = tr[k].w = 1 ;
        tr[k].v = x ;
        tr[k].rnd = rand() ;
        return ;
    }
    tr[k].size ++ ;
    if(tr[k].v==x){tr[k].w++;return;}
    if(x<tr[k].v){insert(tr[k].l,x);if(tr[tr[k].l].rnd<tr[k].rnd){rturn(k);}}
    else{insert(tr[k].r,x);if(tr[tr[k].r].rnd<tr[k].rnd){lturn(k);}}
}
void del(int &k , int x)
{
    if(tr[k].v==x)
    {
        if(tr[k].w>1){tr[k].w--;tr[k].size--;return;}
        if(tr[k].l*tr[k].r==0)k=tr[k].l+tr[k].r;
        else if(tr[tr[k].l].rnd<tr[tr[k].r].rnd){rturn(k);del(k,x);}
        else{lturn(k);del(k,x);}
    }
    else if(x<tr[k].v){del(tr[k].l,x);tr[k].size--;}
    else{del(tr[k].r,x);tr[k].size--;}
}
void build(int k,int l,int r,int x,int num)
{
    insert(root[k],num) ;
    if(l==r)return ;
    int mid = (l+r)>>1 ;
    if(x<=mid)build(k<<1,l,mid,x,num);
    else build(k<<1|1,mid+1,r,x,num);
}

求第k的排名时,treap的rank查询没有区别,然而线段树部分的查询两种情况

1.如果是整区间直接返回treap的查询;

2.不整区间分段来搞,

而查询排名为k的值的时候,采用二分来搞

// 查询在区间排名。
void tr_rk(int k , int x)
{
    if(!k) return ;
    if(tr[k].v==x){ans+=tr[tr[k].l].size;return;}
    else if(x<tr[k].v)tr_rk(tr[k].l,x);
    else {ans+=tr[tr[k].l].size+tr[k].w;tr_rk(tr[k].r,x);}
}
void seg_rk(int k,int l,int r,int L,int R,int x)
{
    if(L==l&&R==r){tr_rk(root[k],x);return;}
    int mid=(l+r)>>1 ;
    if(mid>=R)seg_rk(k<<1,l,mid,L,R,x);
    else if(mid<L)seg_rk(k<<1|1,mid+1,r,L,R,x) ;
    else
    {
        seg_rk(k<<1,l,mid,L,mid,x);
        seg_rk(k<<1|1,mid+1,r,mid+1,R,x);
    }
}
void query_val(int L,int R,int k)
{
    int l=0,r=INF,tmp;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        ans=1,seg_rk(1,1,n,L,R,mid);
        if(ans<=k){l=mid+1,tmp=mid;}
        else r=mid-1;
    }
    printf("%d\n" , tmp);
} 

前驱后继跟k的排名类似。

// treap的前驱后继
void tr_pre(int k,int num)
{
    if(!k)return;
    if(tr[k].v<num){ans=max(ans,tr[k].v);tr_pre(tr[k].r,num);}
    else tr_pre(tr[k].l,num);
}
void tr_sub(int k,int num)
{
    if(!k)return;
    if(tr[k].v>num){ans=min(ans,tr[k].v);tr_sub(tr[k].l,num);}
    else tr_sub(tr[k].r,num);
}
// 求前驱
void query_pre(int k,int l,int r,int L,int R,int x)
{
    if(l==L&&r==R){tr_pre(root[k],x);return;}
    int mid=(l+r)>>1;
    if(mid>=R)query_pre(k<<1,l,mid,L,R,x);
    else if(mid<L)query_pre(k<<1|1,mid+1,r,L,R,x);
    else
    {
        query_pre(k<<1,l,mid,L,mid,x);
        query_pre(k<<1|1,mid+1,r,mid+1,R,x);
    }
}
// 求后继
void query_sub(int k,int l,int r,int L,int R,int x)
{
    if(l==L&&r==R){tr_sub(root[k],x);return;}
    int mid=(l+r)>>1;
    if(mid>=R)query_sub(k<<1,l,mid,L,R,x);
    else if(mid<L)query_sub(k<<1|1,mid+1,r,L,R,x);
    else
    {
        query_sub(k<<1,l,mid,L,mid,x);
        query_sub(k<<1|1,mid+1,r,mid+1,R,x);
    }
}

主函数for循环建树

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)build(1,1,n,i,a[i]);
    for(int i=1;i<=m;i++)
    {
        int jd;
        scanf("%d",&jd);
        int x,y,k;
        switch(jd)
        {
            case 1:scanf("%d%d%d",&x,&y,&k);ans=1;seg_rk(1,1,n,x,y,k);printf("%d\n",ans);break;
            case 2:scanf("%d%d%d",&x,&y,&k);query_val(x,y,k);break;
            case 3:scanf("%d%d",&x,&y);update(1,1,n,x,y,a[x]);a[x]=y;break;
            case 4:scanf("%d%d%d",&x,&y,&k);ans=0;query_pre(1,1,n,x,y,k);printf("%d\n",ans);break;
            case 5:scanf("%d%d%d",&x,&y,&k);ans=INF;query_sub(1,1,n,x,y,k);printf("%d\n",ans);break;
        }
    }
    return 0;
}

至于空间计算,也不是很清楚,大概应该是4*n*lg(4n)+m这样的treap区间。

而线段树依然为4*n.

时间: 2024-10-06 05:54:26

BZOJ 3196 二逼平衡树 线段树+treap的相关文章

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.查询区间内排名为

[BZOJ 3196] 二逼平衡树 树状数组套主席树

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

BZOJ 3196 二逼平衡树 树套树(线段树套Treap)

题目大意: 写一种数据结构,他可以: 1.查询k在区间内的排名. 2.查询区间内排名为k的值 3.修改某一个值. 4.求k在区间内的前驱. 5.求k在区间内的后继. 思路:本来以为有什么只有神犇才知道的神一般的数据结构来维护它,问了别人之后,发现只是树套树.据说怎么套都行.我见识鄙陋,就只能线段树套Treap了. 这也是第一次写树套树,还1A了,有点开心. 写树套树,一定要确定自己对这两个树及其熟练,加上少量精细的思考,就可以完成树套树.(我只是弱渣,求神犇别D) 具体实现:第一层是线段树,第二

BZOJ 3196 二逼平衡树 树套树

题目大意:...BZOJ挂了自己看去 好吧既然BZOJ挂了我还是贴上来吧0.0 破服务器 维护一种数据结构,提供下列操作: 1.查询k在区间内的排名 2.查询区间内排名为k的值 3.修改某一位值上的数值 4.查询k在区间内的前驱(前驱定义为小于x,且最大的数) 5.查询k在区间内的后继(后继定义为大于x,且最小的数) 其实一开始觉得这题是划分树主席树之类的 然后去了解了一下发现完全写不了... 后来才知道原来是树套树 以前想过线段树套树状数组 这数据范围别说树套树了连树状数组都开不开 正解应该是

【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 则为操

BZOJ 3196 二逼平衡树

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]的排名

bzoj3196 二逼平衡树 线段树套平衡树

题意:您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 1.查询k在区间内的排名 2.查询区间内排名为k的值 3.修改某一位值上的数值 4.查询k在区间内的前驱(前驱定义为小于x,且最大的数) 5.查询k在区间内的后继(后继定义为大于x,且最小的数) 题解:树套树,外层是一棵线段树,每个节点下有一棵平衡树(平衡树记录ls,rs,因此记录根节点就可以遍历整棵树),先不考虑空间问题,ask(l,r)可以分成多个线段树区间,每个区间下有平衡树可以查询排名,1&3-5操

[bzoj3196][Tyvj 1730][二逼平衡树] (线段树套treap)

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在区间

[TYVJ 1730]二逼平衡树 线段树套平衡树

来一发树套树.1A也是很感动QAQ 就是时间复杂度略大.而且好像还有其他打法. 谨以此纪念此类型树套树入门 #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> using namespace std; #define pos(i,a,b) for(int i=(a);i<=(b);i++) #define N 51000 #define size(x) ((x)