码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2015-05-25 22:36:28      阅读:129      评论:0      收藏:0      [点我收藏+]

标签:平衡树   树套树   bzoj   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.

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

标签:平衡树   树套树   bzoj   treap   线段树   

原文地址:http://blog.csdn.net/wzq_qwq/article/details/45973113

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!