bzoj 3223 文藝平衡樹 Splay詳細解析

本文不會過於深入介紹splay的證明,感性認知- -。
對於樹我們知道它有兩個旋轉,對於一個節點,如果它是左兒子,那麼它只能右旋,如果它是右兒子,它只能左旋,在splay的時候,我們的目的是把一個節點x轉到一個根上,我們考慮兩種情況,
1在三點一線的時候(3個點沒有彎曲)那麼先旋轉x的父親,在旋轉x,除此之外都直接旋轉x就好了,這樣是爲了讓樹更平衡。(畫個圖感性認知一下0 0)

splay大概流程

void rotate(int x,int &k){
    int y=t[x].fa,z=t[y].fa,p;
    if(t[y].ch[0]==x)p=1;else p=0;
    if(y==k)k=x;
    else {if(t[z].ch[0]==y)t[z].ch[0]=x;else t[z].ch[1]=x;}
    t[x].fa=z;t[y].fa=x;t[y].ch[p^1]=t[x].ch[p];
    t[t[x].ch[p]].fa=y;t[x].ch[p]=y;
    update(x);update(y);
}
void splay(int x,int &k){
    while(x!=k){
        int y=t[x].fa,z=t[y].fa;
        if(y!=k){
            if(t[y].ch[0]==x^t[z].ch[0]==y)rotate(x,k);
            else rotate(y,k);
        }
        rotate(x,k);
    }
}

然後我們來分析這道題
對於本題來說,我們需要翻轉區間。首先我們考慮在數組上,每一個位置對應一個值,如果翻轉區間[ L, R ] 就是把所有大於l-1 並且小於r+1 的位置翻轉,那麼我們很自然的想到bst的性質,然後想到平衡樹(根據題目名稱可知)。
然後把翻轉轉到樹上就是交換節點的左右子樹,然後遞歸下去交換。
接下來就比較繞了,爲什麼交換左右子樹能保持bst性質呢?如何找到樹上某個點呢?

因爲我們要找的是在數組上的位置,所以應該是第幾個點,而不是第幾號點,所以只有在最開始的時候節點編號才恰好爲第幾個點。那麼其實在每次交換子樹的時候,我們其實就是在原數組中該位置的點左邊部分和右邊部分給交換了位置。所以能解釋它爲什麼兩邊siz都變了還能滿足bst性質。

更可以這麼理解的是,每次交換後,都相當於改變了一次每個點是第幾號點。
例如1,2,3,4,5我們交換了2到4,變爲1,4,3,2,5我們可以發現4它從第4個點變成了第2個點。自動就改變了,是不是很神奇O(∩_∩)O哈哈~

下面是我A掉的代碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
using namespace std;
int n,m,rt;
struct date{
    int ch[2],fa,lazy,siz;
    date(){ch[0]=-1;ch[1]=-1;}
};
date t[N];
void update(int x){
    int l=t[x].ch[0],r=t[x].ch[1];
    t[x].siz=t[l].siz+t[r].siz+1;
}
void pushdown(int k){
    if(t[k].lazy){
        int &l=t[k].ch[0],&r=t[k].ch[1];
        swap(l,r);
        t[l].lazy^=1,t[r].lazy^=1,t[k].lazy^=1;
    }
}
void rotate(int x,int &k){
    int y=t[x].fa,z=t[y].fa,p;
    if(t[y].ch[0]==x)p=1;else p=0;
    if(y==k)k=x;
    else {if(t[z].ch[0]==y)t[z].ch[0]=x;else t[z].ch[1]=x;}
    t[x].fa=z;t[y].fa=x;t[y].ch[p^1]=t[x].ch[p];
    t[t[x].ch[p]].fa=y;t[x].ch[p]=y;
    update(x);update(y);
}
void splay(int x,int &k){
    while(x!=k){
        int y=t[x].fa,z=t[y].fa;
        if(y!=k){
            if(t[y].ch[0]==x^t[z].ch[0]==y)rotate(x,k);
            else rotate(y,k);
        }
        rotate(x,k);
    }
}
int build(int l,int r,int f){
    if(l>r)return -1;
    if(l==r){
        t[l].fa=f,t[l].siz=1,t[l].lazy=0;
        return l;
    }
    else{
        int mid=(l+r)>>1;
        t[mid].ch[0]=build(l,mid-1,mid);
        t[mid].ch[1]=build(mid+1,r,mid);
        t[mid].fa=f,t[mid].lazy=0;
        update(mid);
        return mid;
    }
}
int find(int k,int val){
    pushdown(k);
    int l=t[k].ch[0],r=t[k].ch[1];
    if(t[l].siz+1==val)return k;
    else if(t[l].siz>=val)return find(l,val);
    else return find(r,val-t[l].siz-1);
}
void rev(int l,int r){
    int x=find(rt,l),y=find(rt,r+2);
    splay(x,rt);splay(y,t[x].ch[1]);
    int z=t[y].ch[0];
    t[z].lazy^=1;
}
int main(){
    int l,r;
    scanf("%d%d",&n,&m);
    rt=build(0,n+1,-1);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&l,&r);
        rev(l,r);
    }
    for(int i=1;i<=n;i++)
    printf("%d ",find(rt,i+1));
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章