[BZOJ3196]二逼平衡树(线段树套splay)

70 篇文章 0 订阅
33 篇文章 0 订阅

题目描述

传送门

题解

线段树套splay,简单地说就是线段树的每一个节点都吊着一颗splay,表示的是线段树当前节点所表示的区间的点,按权值排序。
Q1:线段树常规查询区间,每一次统计小于k的点的个数再相加。
Q2:这个是最麻烦也是最精妙的一问,解决方法是二分答案,每二分到一个答案查询一下这个答案在这个区间内的排名,如果排名等于k+1的话返回它的pre即可。注意这里二分满足条件之后不用查询pre,答案直接为head-1,可以证明head-1一定在序列中。
Q3:相当于线段树的点修改,在splay中删除再插入即可。
Q4:线段树常规查询区间,每一次找区间内比k小的最大的数,然后取max
Q5:类似于Q4,每一次找区间内比k大的最小的数,然后取min
树套树写起来还是比较好写的,但是刚开始姿势不对,尤其是Q2Q4Q5都有比较快的姿势。po主的代码在BZOJ上评测过了,但是tyvjT2组只好弃疗T_T
具体看代码吧,以SegTree为前缀的都是线段树的操作,以Splay为前缀的都是splay的操作,其余的都用比较简单易懂的英文表示w

代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

const int max_n=4e6+5;
const int INF=1e9;

int n,m,opt,l,r,k,pos,sz,ans,maxn;
int seq[max_n],f[max_n],ch[max_n][2],size[max_n],cnt[max_n],key[max_n],root[max_n];

inline int in(){
    int x=0,f=1; char ch=getchar();
    while (ch>'9'||ch<'0'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
inline void Splay_clear(int x){
    f[x]=ch[x][0]=ch[x][1]=size[x]=cnt[x]=key[x]=0;
}
inline int Splay_get(int x){
    return ch[f[x]][1]==x;
}
inline void Splay_update(int x){
    if (x){
        size[x]=cnt[x];
        if (ch[x][0]) size[x]+=size[ch[x][0]];
        if (ch[x][1]) size[x]+=size[ch[x][1]];
    }
}
inline void Splay_rotate(int x){
    int old=f[x],oldf=f[old],which=Splay_get(x);
    ch[old][which]=ch[x][which^1];
    f[ch[old][which]]=old;
    ch[x][which^1]=old;
    f[old]=x;
    if (oldf) ch[oldf][ch[oldf][1]==old]=x;
    f[x]=oldf;
    Splay_update(old);
    Splay_update(x);
}
inline void Splay_splay(int x){
    for (int fa;fa=f[x];Splay_rotate(x))
      if (f[fa])
        Splay_rotate((Splay_get(x)==Splay_get(fa))?fa:x);
}
inline void Splay_insert(int i,int x){
    int now=root[i],fa=0;
    if (!root[i]){
        root[i]=++sz;
        f[sz]=ch[sz][0]=ch[sz][1]=0;
        size[sz]=cnt[sz]=1; key[sz]=x;
        return;
    }
    while (1){
        if (x==key[now]){
            cnt[now]++;
            Splay_update(fa);
            Splay_splay(now);
            root[i]=now;
            return;
        }
        fa=now;
        now=ch[now][key[now]<x];
        if (!now){
            ++sz;
            f[sz]=fa; ch[sz][0]=ch[sz][1]=0;
            size[sz]=cnt[sz]=1; key[sz]=x;
            ch[fa][key[fa]<x]=sz;
            Splay_update(fa);
            Splay_splay(sz);
            root[i]=sz;
            return;
        }
    }
}
inline void Splay_find(int i,int x){
    int now=root[i];
    while (1){
        if (key[now]==x){
            Splay_splay(now);
            root[i]=now;
            return;
        }
        else if (key[now]>x) now=ch[now][0];
        else if (key[now]<x) now=ch[now][1];
    }
}
inline int Splay_pre(int i){
    int now=ch[root[i]][0];
    while (ch[now][1]) now=ch[now][1];
    return now;
}
inline int Splay_next(int i){
    int now=ch[root[i]][1];
    while (ch[now][0]) now=ch[now][0];
    return now;
}
inline void Splay_del(int i){
    int now=root[i];
    if (cnt[now]>1){
        cnt[now]--;
        Splay_update(now);
        return;
    }
    if (!ch[now][0]&&!ch[now][1]){
        Splay_clear(root[i]);
        root[i]=0;
        return;
    }
    if (!ch[now][0]){
        int oldroot=now;
        root[i]=ch[oldroot][1];
        f[root[i]]=0;
        Splay_clear(oldroot);
        return;
    }
    if (!ch[now][1]){
        int oldroot=now;
        root[i]=ch[oldroot][0];
        f[root[i]]=0;
        Splay_clear(oldroot);
        return;
    }
    int leftbig=Splay_pre(i),oldroot=root[i];
    Splay_splay(leftbig); root[i]=leftbig;
    ch[root[i]][1]=ch[oldroot][1];
    f[ch[oldroot][1]]=root[i];
    Splay_clear(oldroot);
    Splay_update(root[i]);
    return;
}
inline int Splay_findrank(int i,int x){
    int now=root[i],ans=0;
    while (1){
        if (!now) return ans;
        if (key[now]==x) return ((ch[now][0])?size[ch[now][0]]:0)+ans;
        else if (key[now]<x){
            ans+=((ch[now][0])?size[ch[now][0]]:0)+cnt[now];
            now=ch[now][1];
        }
        else if (key[now]>x) now=ch[now][0];
    }
}
inline int Splay_findpre(int i,int k){
    int now=root[i];
    while (now){
        if (key[now]<k){
            if (ans<key[now])ans=key[now];
            now=ch[now][1];
        }
        else now=ch[now][0];
    }
    return ans;
}
inline int Splay_findnext(int i,int k){
    int now=root[i];
    while (now){
        if (key[now]>k){
            if (ans>key[now]) ans=key[now];
            now=ch[now][0];
        }
        else now=ch[now][1];
    }
    return ans;
}
inline void SegTree_insert(int now,int l,int r,int x,int v){
    int mid=(l+r)>>1;
    Splay_insert(now,v);
    if (l==r) return;
    if (x<=mid) SegTree_insert(now<<1,l,mid,x,v);
    else SegTree_insert(now<<1|1,mid+1,r,x,v);
}
inline void SegTree_askrank(int now,int l,int r,int lrange,int rrange,int k){
    int mid=(l+r)>>1;
    if (lrange<=l&&r<=rrange){
        ans+=Splay_findrank(now,k);
        return;
    }
    if (lrange<=mid) SegTree_askrank(now<<1,l,mid,lrange,rrange,k);
    if (mid+1<=rrange) SegTree_askrank(now<<1|1,mid+1,r,lrange,rrange,k);
}
inline void SegTree_change(int now,int l,int r,int pos,int k){
    int mid=(l+r)>>1;
    Splay_find(now,seq[pos]); Splay_del(now); Splay_insert(now,k);
    if (l==r) return;
    if (pos<=mid) SegTree_change(now<<1,l,mid,pos,k);
    else SegTree_change(now<<1|1,mid+1,r,pos,k);
}
inline void SegTree_askpre(int now,int l,int r,int lrange,int rrange,int k){
    int mid=(l+r)>>1;
    if (lrange<=l&&r<=rrange){
        ans=max(ans,Splay_findpre(now,k));
        return;
    }
    if (lrange<=mid) SegTree_askpre(now<<1,l,mid,lrange,rrange,k);
    if (mid+1<=rrange) SegTree_askpre(now<<1|1,mid+1,r,lrange,rrange,k);
}
inline void SegTree_asknext(int now,int l,int r,int lrange,int rrange,int k){
    int mid=(l+r)>>1;
    if (lrange<=l&&r<=rrange){
        ans=min(ans,Splay_findnext(now,k));
        return;
    }
    if (lrange<=mid) SegTree_asknext(now<<1,l,mid,lrange,rrange,k);
    if (mid+1<=rrange) SegTree_asknext(now<<1|1,mid+1,r,lrange,rrange,k);
}
int main(){
    n=in(); m=in();
    for (int i=1;i<=n;++i) seq[i]=in(),maxn=max(maxn,seq[i]),SegTree_insert(1,1,n,i,seq[i]);
    for (int i=1;i<=m;++i){
        opt=in();
        switch(opt){
            case 1:{
                l=in(); r=in(); k=in();
                ans=0;
                SegTree_askrank(1,1,n,l,r,k);
                printf("%d\n",ans+1);
                break;
            }
            case 2:{
                l=in(); r=in(); k=in();
                int head=0,tail=maxn+1,mid;
                while (head!=tail){
                    int mid=(head+tail)>>1;
                    ans=0;
                    SegTree_askrank(1,1,n,l,r,mid);
                    if (ans<k) head=mid+1;
                    else tail=mid;
                }
                printf("%d\n",head-1);
                break;     
            }
            case 3:{
                pos=in(); k=in();
                SegTree_change(1,1,n,pos,k);
                seq[pos]=k;
                maxn=max(maxn,k);
                break;
            }
            case 4:{
                l=in(); r=in(); k=in();
                ans=0;
                SegTree_askpre(1,1,n,l,r,k);
                printf("%d\n",ans);
                break;
            }
            case 5:{
                l=in(); r=in(); k=in();
                ans=INF;
                SegTree_asknext(1,1,n,l,r,k);
                printf("%d\n",ans);
                break;
            }
        }
    }
}

总结

Orz xym 优越的姿势

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值