splay樹(修訂版)

伸展樹

多值版

#include<bits/stdc++.h>
using namespace std;
template<class T> inline bool read(T &x){
    x=0;register char c=getchar();register bool f=0;
    while(!isdigit(c)){if(c==EOF)return false;f^=c=='-',c=getchar();}
    while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
    if(f)x=-x;
    return true;
}
template<class T>inline void print(T x){
    if(x<0)putchar('-'),x=-x;
    if(x>9)print(x/10);
    putchar('0'+x%10);
}
typedef long long ll;
const ll MAXN=1e5+8,inf=0x3f3f3f3f,mod=1e9+7;
struct Node {
    int val,num,son_num;
    int fa,lson,rson;
    Node() {val=num=son_num=fa=rson=lson=0;}
    Node(int f,int ls,int rs,int v,int n,int sn):
        fa(f),lson(ls),rson(rs),val(v),num(n),son_num(sn) {}
}tree[MAXN];
int root,cnt;
inline void init(){root=cnt=0;}
inline bool islson(int x){return tree[tree[x].fa].lson==x?1:0;}
inline void update(int x){
    if(!x)return;
    tree[x].son_num=tree[x].num;
    if(tree[x].lson)tree[x].son_num+=tree[tree[x].lson].son_num;
    if(tree[x].rson)tree[x].son_num+=tree[tree[x].rson].son_num;
}
inline void rotate(int x){//默認x有父節點
    int f=tree[x].fa,ff=tree[f].fa;//ff可能是0
    if(islson(x)){//zig 右旋
        tree[f].lson=tree[x].rson,tree[tree[x].rson].fa=f;
        tree[x].rson=f,tree[f].fa=x;
    }else{//zag 左旋
        tree[f].rson=tree[x].lson,tree[tree[x].lson].fa=f;
        tree[x].lson=f,tree[f].fa=x;
    }
    tree[x].fa=ff;
    //必須這樣判斷,不能用islson,因爲f的父節點已經是x了
    if(tree[ff].lson==f)tree[ff].lson=x;
    else tree[ff].rson=x;
    update(f);//只需更新深度更大的f,x之後更新
}
inline void splay(int x,int end_pos) {
    int f=tree[x].fa;
    while(f^end_pos) {
        if(tree[f].fa^end_pos) {//如果f不是根
            if(islson(x)^islson(f))rotate(x);//zig+zag or zag+zig
            else rotate(f);//zig+zig or zag+zag
        }rotate(x);
        f=tree[x].fa;
    }
    update(x);//最上層節點還沒更新
    root=x;
}
void insert(int v){
    if(!root){//如果有節點。
        tree[++cnt]=Node(0,0,0,v,1,1);
        root=cnt;
        return;
    }
    int now=root,f=0;
    while(1){
        if(tree[now].val==v){//原來有val節點
            tree[now].num++;
            tree[now].son_num++;
            splay(now,0);
            break;
        }
        f=now;
        if(v<tree[now].val)now=tree[now].lson;
        else now=tree[now].rson;
        if(now==0){//創建節點
            tree[++cnt]=Node(f,0,0,v,1,1);//父節點是f
            if(v>tree[f].val)tree[f].rson=cnt;
            else tree[f].lson=cnt;
            splay(cnt,0);
            break;
        }
    }
}
inline int pre(){//返回根節點的val值的前繼的節點編號
    int now=tree[root].lson;
    while(tree[now].rson)now=tree[now].rson;
    return now;
}
inline int nxt(){//後繼
    int now=tree[root].rson;
    while(tree[now].lson)now=tree[now].lson;
    return now;
}
int find_val_rank(int val){//返回值爲val的最小的排名
    int now=root,ans=0;
    while(1){
        //如果當前節點值大於val,進入左子樹
        if(val<tree[now].val)now=tree[now].lson;
        else{//否則,進入右子樹
            //加上值絕對小於val的數量
            if(tree[now].lson)ans+=tree[tree[now].lson].son_num;
            if(tree[now].val==val){
                splay(now,0);
                return ans+1;
            }
            //否則,當前節點值小於val,加上
            ans+=tree[now].num;
            now=tree[now].rson;
        }
    }

}
/*
將要刪除的節點旋轉到root
考慮刪除root
1. 如果root有多餘1次出現,直接num--
2. 如果左右子樹都爲空,變成空樹
3. 只有一個兒子
4. 有兩個兒子
*/
inline void del(int val){/
    find_val_rank(val);
    if(tree[root].num>1){//如果大於一個,直接減少一個。
        tree[root].num--;
        tree[root].son_num--;
        return;
    }
    if(!tree[root].lson&&!tree[root].rson){//如果左右子樹都爲空,則樹爲空
        root=cnt=0;
        return;
    }
    if(!tree[root].lson){//如果沒有左兒子,直接將右兒子作爲根
        root=tree[root].rson;
        tree[root].fa=0;
        return;
    }
    if(!tree[root].rson){//如果沒有右兒子,將左兒子作爲根
        root=tree[root].lson;
        tree[root].fa=0;
        return;
    }
    //左右兒子都存在,尋找前繼,把它設爲根
    //這樣的話,原來的根就沒有左子樹了
    //未開始操作前,新的root的右兒子就是原來的根
    int old_root=root,pre_node=pre();
    splay(pre_node,0);
    tree[root].rson=tree[old_root].rson;
    tree[tree[old_root].rson].fa=root;
    update(root);
}
//返回第kth名的值
int find_kth_rank(int kth){
    int now=root;
    while(1){
        //如果now有左兒子且左兒子裏節點數大於kth,答案肯定在左兒子
        if(tree[now].lson&&kth<=tree[tree[now].lson].son_num)
            now=tree[now].lson;
        else{//在右兒子或now
            int ls=tree[now].lson,sum=tree[now].num;
            if(ls)sum+=tree[ls].son_num;//有左兒子,sum加上左兒子裏的數量
            if(kth<=sum)return tree[now].val;
            //只有左兒子kth>sum,加上now就符合,所以正確。
            kth-=sum;//減去左兒子和now節點的的數量,進入右兒子
            now=tree[now].rson;
        }
    }
}
int main() {
    int n,op,x;
    read(n);
    while(n--){
        read(op),read(x);
        if(op==1)insert(x);
        else if(op==2)del(x);
        else if(op==3)printf("%d\n",find_val_rank(x));
        else if(op==4)printf("%d\n",find_kth_rank(x));
        else if(op==5){
            insert(x);//查詢的時候先添加,這樣就必定有x節點,就能求出x的前繼了
            printf("%d\n",tree[pre()].val);
            del(x);
        }else{
            insert(x);
            printf("%d\n",tree[nxt()].val);
            del(x);
        }
    }
    return 0;
}

單值版

struct Splay{
    int val[MAXN],num[MAXN],ls[MAXN],rs[MAXN],fa[MAXN];
    inline void up(int x){//因爲所有節點代表一個值,所以最初是1
        num[x]=1;
        if(ls[x])num[x]+=num[ls[x]];
        if(rs[x])num[x]+=num[rs[x]];
    }
    int cnt,root;
    inline int build(int l,int r,int f){
        //建樹,[l,mid-1],mid,[mid+1,r],保證中序遍歷是原序列
        int mid=(l+r)>>1;
        int id=++cnt;//用另一個變量存儲cnt,遞歸先改變cnt,再return
        num[id]=1,fa[id]=f;
        scanf("%d",val+id);
        if(l==r){ls[id]=rs[cnt]=0;return id;}
        if(l<mid)ls[id]=build(l,mid-1,id);
        if(r>mid)rs[id]=build(mid+1,r,id);
        return up(id),id;
    }
    inline bool islson(int x){return ls[fa[x]]==x?1:0;}
    inline void rotate(int x){//單旋
        int f=fa[x],ff=fa[f];
        if(islson(x)){//zig
            ls[f]=rs[x],fa[rs[x]]=f;
            rs[x]=f,fa[f]=x;
        }else{//zag
            rs[f]=ls[x],fa[ls[x]]=f;
            ls[x]=f,fa[f]=x;
        }
        fa[x]=ff;
        if(ls[ff]==f)ls[ff]=x;
        else rs[ff]=x;
        up(f);//只要更新x下方節點
    }
    void splay(int x,int pos){//伸展爲pos的子節點
        int f=fa[x],ff=fa[f];
        while(f^pos){
            if(ff^pos){//如果ff就是pos,只需要再旋轉一次即可。
                if(islson(x)^islson(f))rotate(x);
                else rotate(f);
            }
            rotate(x),f=fa[x],ff=fa[f];
        }up(x);
        if(!pos)root=x;//pos可能不爲零
    }
    void insert(int vv){
        cnt++;
        if(!root){
            val[cnt]=vv,num[cnt]=1,root=cnt;
            return;
        }
        int x=root,f;
        while(x){
            f=x;
            if(vv<val[x])x=ls[x];
            else x=rs[x];
        }
        fa[cnt]=f,val[cnt]=vv,num[cnt]=1;
        if(vv>val[f])rs[f]=cnt;
        else ls[f]=cnt;
        splay(cnt,0);
    }
    inline int find_kth(int k){//返回第k個節點編號
        int x=root;
        while(1){
            if(ls[x]&&k<=num[ls[x]])x=ls[x];
            else{
                int sum=1;//每個節點都是唯一值,不會重複出現。
                if(ls[x])sum+=num[ls[x]];
                if(k<=sum)return splay(x,0),x;
                //一定要splay,不然單調數據卡爆
                k-=sum;
                x=rs[x];
            }
        }
    }
}

伸展樹(區間旋轉)

交換節點下面的所有左右兒子,則中序遍歷結果和原序列相反,即旋轉。
思路:要旋轉的區間爲[l,r],將l-1代表的點旋轉到root,再將r+1代表的點旋轉到root的右兒子處,則:
x代表的是區間內的點 <=> x在r+1代表的點的左子樹裏面

//a序列m次翻轉
#include<bits/stdc++.h>
using namespace std;
template<class T> inline bool read(T &x){
    x=0;register char c=getchar();register bool f=0;
    while(!isdigit(c)){if(c==EOF)return false;f^=c=='-',c=getchar();}
    while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
    if(f)x=-x;
    return true;
}
template<class T>inline void print(T x){
    if(x<0)putchar('-'),x=-x;
    if(x>9)print(x/10);
    putchar('0'+x%10);
}
typedef long long ll;
const ll MAXN=1e5+8,inf=0x3f3f3f3f,mod=1e9+7;
int v[MAXN],num[MAXN],ls[MAXN],rs[MAXN],fa[MAXN];
int a[MAXN],n,m,cnt,root;
bool flag[MAXN];
inline void up(int x){//因爲所有節點代表一個值,所以最初是1
    num[x]=1;
    if(ls[x])num[x]+=num[ls[x]];
    if(rs[x])num[x]+=num[rs[x]];
}
inline void down(int x){//下放標記
    swap(ls[x],rs[x]);
    flag[ls[x]]^=1;
    flag[rs[x]]^=1;
    flag[x]=0;
}
inline bool islson(int x){return ls[fa[x]]==x?1:0;}
inline int build(int l,int r,int f){
    //建樹,[l,mid-1],mid,[mid+1,r],保證中序遍歷是原序列
    int mid=(l+r)>>1;
    int id=++cnt;//用另一個變量存儲cnt,因爲遞歸先改變cnt,再return
    num[id]=1;
    fa[id]=f;
    v[id]=a[mid];
    if(l==r){ls[id]=rs[cnt]=0;return id;}
    if(l<mid)ls[id]=build(l,mid-1,id);
    if(r>mid)rs[id]=build(mid+1,r,id);
    up(id);
    return id;
}
inline void rotate(int x){//單旋
    int f=fa[x],ff=fa[f];
    if(islson(x)){//zig
        ls[f]=rs[x],fa[rs[x]]=f;
        rs[x]=f,fa[f]=x;
    }else{//zag
        rs[f]=ls[x],fa[ls[x]]=f;
        ls[x]=f,fa[f]=x;
    }
    fa[x]=ff;
    if(ls[ff]==f)ls[ff]=x;
    else rs[ff]=x;
    up(f);//值更新x下方節點
}
inline void splay(int x,int end_pos){
    int f=fa[x],ff=fa[f];
    while(f^end_pos){
        if(flag[f])down(f);//因爲要改變f和x的位置,所以要先putdown
        if(flag[x])down(x);
        if(ff^end_pos){//這裏和其他不同,如果ff就是end_pos,只需要再旋轉一次即可。
            if(islson(x)^islson(f))rotate(x);
            else rotate(f);
        }
        rotate(x);
        f=fa[x];ff=fa[f];
    }
    up(x);
    if(!end_pos)root=x;//end_pos可能不爲零
}
inline int find_kth(int k){//返回第k個節點編號
    int x=root;
    while(1){
        if(flag[x])down(x);
        if(ls[x]&&k<=num[ls[x]])x=ls[x];
        else{
            int sum=1;//每個節點都是唯一值,不會重複出現。
            if(ls[x])sum+=num[ls[x]];
            if(k<=sum)return x;
            k-=sum;
            x=rs[x];
        }
    }
}
inline void change(int x,int y){
    int l=find_kth(x-1),r=find_kth(y+1);
    //把第x-1個節點旋轉至root
    splay(l,0);
    splay(r,root);//第y+1個節點旋轉至root右兒子的左兒子
    flag[ls[rs[root]]]^=1;
}
void get_ans(int x){//中序遍歷
    if(flag[x])down(x);
    if(ls[x])get_ans(ls[x]);
    a[++cnt]=v[x];
    if(rs[x])get_ans(rs[x]);
}
int main() {
    read(n),read(m);
    for(int i=1;i<=n+2;++i)a[i]=i-1;
    root=build(1,n+2,0);
    int x,y;
    while(m--){
        read(x),read(y);
        change(x+1,y+1);
    }
    cnt=0;//下面重新用到了cnt
    get_ans(root);
    for(int i=2;i<=n+1;++i)printf("%d ",a[i]);
    return 0;
}

POJ-3481

#include<iostream>
using namespace std;
template<class T> inline bool read(T &x){
    x=0;register char c=getchar();register bool f=0;
    while(!isdigit(c)){if(c==EOF)return false;f^=c=='-',c=getchar();}
    while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
    if(f)x=-x;
    return true;
}
template<class T>inline void print(T x){
    if(x<0)putchar('-'),x=-x;
    if(x>9)print(x/10);
    putchar('0'+x%10);
}
typedef long long ll;
const ll MAXN=1e6+8,inf=0x3f3f3f3f,mod=1e9+7;
int v[MAXN],num[MAXN],ls[MAXN],rs[MAXN],fa[MAXN],cnt,root;
int id[MAXN];
inline bool islson(int x){return ls[fa[x]]==x?1:0;}
inline void update(int x){
    num[x]=1;
    if(ls[x])num[x]+=num[ls[x]];
    if(rs[x])num[x]+=num[rs[x]];
}
inline void rotate(int x){
    int f=fa[x],ff=fa[f];
    if(islson(x)){
        ls[f]=rs[x],fa[rs[x]]=f;
        rs[x]=f,fa[f]=x;
    }else{
        rs[f]=ls[x],fa[ls[x]]=f;
        ls[x]=f,fa[f]=x;
    }
    fa[x]=ff;
    if(ls[ff]==f)ls[ff]=x;
    else rs[ff]=x;
    update(f);
}
inline void splay(int x,int end_pos){
    int f=fa[x];
    while(f^end_pos){
        if(fa[f]){
            if(islson(x)^islson(f))rotate(x);
            else rotate(f);
        }
        rotate(x);
        f=fa[x];
    }
    update(x);
    if(!end_pos)root=x;
}
inline void insert(int val,int i){
    if(!root){
        num[++cnt]=1;v[cnt]=val;id[cnt]=i;
        root=cnt;fa[cnt]=0;
        return;
    }
    int x=root;
    while(1){
        if(val<v[x]){
            if(ls[x])x=ls[x];
            else{
                num[++cnt]=1;v[cnt]=val;id[cnt]=i;
                fa[cnt]=x;ls[x]=cnt;
                splay(cnt,0);
                return;
            }
        }
        else{
            if(rs[x])x=rs[x];
            else{
                num[++cnt]=1;v[cnt]=val;id[cnt]=i;
                fa[cnt]=x;rs[x]=cnt;
                splay(cnt,0);
                return;
            }
        }
    }
}
inline int get_high(){
    if(!root)return 0;
    int x=root;
    while(1){
        if(rs[x])x=rs[x];
        else return x;
    }
}
inline int get_low(){
    if(!root)return 0;
    int x=root;
    while(1){
        if(ls[x])x=ls[x];
        else return x;
    }
}
inline int get_h_del(){
    int res_pos=get_high();
    splay(res_pos,0);
    root=ls[res_pos];
    fa[root]=0;
    return id[res_pos];
}
inline int get_l_del(){
    int res_pos=get_low();
    splay(res_pos,0);
    root=rs[res_pos];
    fa[root]=0;
    return id[res_pos];
}
int main() {
    int op,k,p,res;
    while(read(op)&&op){
        if(op==1){
            read(k),read(p);
            insert(p,k);
        }
        else if(op==2)printf("%d\n",get_h_del());
        else if(op==3)printf("%d\n",get_l_del());
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章