伸展樹(Splay)

codevs 4655序列終結者
Splay樹是一個優化過的二叉搜索樹,所以它滿足二叉搜索樹的性質,二叉搜索樹有可能退化爲一條鏈,n^2的時間複雜度,而Splay經過一系列的旋轉操作使其平均複雜度控制在log(2)n但是常數比較大,因爲要進行一系列的旋轉操作。

/*
因爲Splay_tree滿足排序二叉樹的性質,所以當查詢或者修改某一個[L,R]區間的時候,
就可以將L旋轉到根的位置,將R旋轉到根的右節點位置這樣R的左節點電錶的區間就是[L,R]這個區間。

建樹的時候從1到n+2每個節點代表一個節點滿足左節點小於根,右節點大於根所以纔會出現這種建樹方式,
每一棵子樹代表一個區間。

splay操作即伸展操作,將某個節點通過一系列的旋轉到達固定節點的子節點位置。
rotate操作即是每一步具體的旋轉操作
*/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=1e6+10;
const int inf=0x7fffffff;
struct Splay_tree
{
    int root;
    struct zp
    {
        int son[2],Size,fa,lazy,rev,Max,val;//左右兒子 ,子樹大小,父節點位置,延遲更新標記,反轉標記,子樹最大值,節點值
        void init(int _val)
        {
            Size=1,Max=val=_val;
            rev=lazy=son[0]=son[1]=0;
        }
    } T[maxn];
    void push_up(int x)
    {
        T[x].Size=1,T[x].Max=T[x].val;
        if(T[x].son[0])
        {
            T[x].Max=max(T[x].Max,T[T[x].son[0]].Max);
            T[x].Size+=T[T[x].son[0]].Size;
        }
        if(T[x].son[1])
        {
            T[x].Max=max(T[x].Max,T[T[x].son[1]].Max);
            T[x].Size+=T[T[x].son[1]].Size;
        }
    }
    void push_down(int x)
    {
        if(x==0) return ;
        if(T[x].lazy)
        {
            if(T[x].son[0])
            {
                T[T[x].son[0]].lazy+=T[x].lazy;
                T[T[x].son[0]].Max+=T[x].lazy;
                T[T[x].son[0]].val+=T[x].lazy;
            }
            if(T[x].son[1])
            {
                T[T[x].son[1]].lazy+=T[x].lazy;
                T[T[x].son[1]].Max+=T[x].lazy;
                T[T[x].son[1]].val+=T[x].lazy;
            }
            T[x].lazy=0;
        }
        if(T[x].rev)
        {
            if(T[x].son[0])
                T[T[x].son[0]].rev^=1;
            if(T[x].son[1])
                T[T[x].son[1]].rev^=1;
            swap(T[x].son[0],T[x].son[1]);
            T[x].rev=0;
        }
    }
    void rotate(int x,int d)//0 左旋 1右旋
    {
        int y=T[x].fa,z=T[y].fa;
        T[y].son[!d]=T[x].son[d];
        T[T[x].son[d]].fa=y;
        T[x].son[d]=y;
        T[y].fa=x;
        T[z].son[T[z].son[1]==y]=x;
        T[x].fa=z;
        push_up(y);
    }
    void splay(int x,int goal)//將x旋轉到goal的子節點的位置 伸展操作
    {
        if(x==goal) return ;
        while(T[x].fa!=goal)
        {
            int y=T[x].fa,z=T[y].fa;
            push_down(z),push_down(y),push_down(x);
            int rx=T[y].son[0]==x,ry=T[z].son[0]==y;//rx是x節點的旋轉方向,ry是y節點的旋轉方向
            if(z==goal) rotate(x,rx);
            else
            {
                if(rx==ry) rotate(y,rx);
                else rotate(x,rx);
                rotate(x,ry);
            }
        }
        push_up(x);
        if(goal==0) root=x;
    }
    int Select(int pos)//實質上是找第pos+1個數的位置在哪
    {
        int u=root;
        push_down(u);
        while(T[T[u].son[0]].Size!=pos)
        {
            if(pos<T[T[u].son[0]].Size)
                u=T[u].son[0];
            else
                pos-=T[T[u].son[0]].Size+1,u=T[u].son[1];
            push_down(u);
        }
        return u;
    }
    void reverse(int l,int r)//反轉區間
    {
        int u=Select(l-1),v=Select(r+1);
        splay(u,0);
        splay(v,u);
        T[T[v].son[0]].rev^=1;
    }
    int build_tree(int l,int r)//建樹
    {
        if(l>r) return 0;
        if(l==r) return l;
        int mid=(l+r)>>1,sl,sr;
        sl=T[mid].son[0]=build_tree(l,mid-1);
        sr=T[mid].son[1]=build_tree(mid+1,r);
        T[sl].fa=T[sr].fa=mid;
        push_up(mid);
        return mid;
    }
    void update(int l,int r,int val)//更新
    {
        int u=Select(l-1),v=Select(r+1);
        splay(u,0);
        splay(v,u);
        T[T[v].son[0]].Max+=val;
        T[T[v].son[0]].val+=val;
        T[T[v].son[0]].lazy+=val;
    }
    int query(int l,int r)//查詢
    {
        int u=Select(l-1),v=Select(r+1);
        splay(u,0);
        splay(v,u);
        return T[T[v].son[0]].Max;
    }
    void init(int n)
    {
        T[0].init(-inf),T[1].init(-inf),T[n+2].init(-inf);
        for(int i=2; i<=n+1; i++) T[i].init(0);
        root=build_tree(1,n+2),T[root].fa=0;
        T[0].fa=0,T[0].son[1]=root,T[0].Size=0;
    }
} ac;
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        ac.init(n);
        while(m--)
        {
            int opt;
            scanf("%d",&opt);
            if(opt==1)
            {
                int l,r,val;
                scanf("%d%d%d",&l,&r,&val);
                ac.update(l,r,val);
            }
            else if(opt==2)
            {
                int l,r;
                scanf("%d%d",&l,&r);
                ac.reverse(l,r);
            }
            else if(opt==3)
            {
                int l,r;
                scanf("%d%d",&l,&r);
                printf("%d\n",ac.query(l,r));
            }
        }
    }
    return 0;
}

bzoj 1208 寵物收養所
這題可以用set寫代碼非常短,但是我在學習splay還是用splay寫了一發,結果無限TLE,最後發現大佬的板子還是不能亂改啊。
set版本

#include<set>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int mod=1e6;
const int inf=0x3f3f3f3f;
int main()
{
    int n;
    scanf("%d",&n);
    set<int> s;
    int flag,sum=0,ans=0;
    s.insert(-inf);
    s.insert(inf);
    while(n--)
    {
        int opt,val;
        scanf("%d%d",&opt,&val);
        set<int>::iterator it;
        if(sum==0)
        {
            flag=opt;
            s.insert(val);
            sum++;
        }
        else if(opt==flag)
        {
            s.insert(val);
            sum++;
        }
        else
        {
            sum--;
            it=s.lower_bound(val);
            int tmp1=*it;
            --it;
            int tmp2=*it;
            if(abs(val-tmp1)<abs(val-tmp2))
                ans=(ans+abs(val-tmp1))%mod,s.erase(++it);
            else
                ans=(ans+abs(val-tmp2))%mod,s.erase(it);
        }
    }
    printf("%d\n",ans);
}

splay版本

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ls(n) T[(n)].son[0]
#define rs(n) T[(n)].son[1]
#define LL long long
using namespace std;
const int maxn=1e6+10;
const LL inf=0x7fffffff;
const int mod=1e6;
int cnt;
struct splay_tree
{
    int root,cont;
    struct zp
    {
        int son[2];
        LL val;
        void init(LL _val)
        {
            val=_val;
            son[0]=son[1]=0;
        }
    } T[maxn];
    int fa[maxn];
    void init()
    {
        cont=1;
        root=0;
        memset(fa,0,sizeof(fa));
    }
    int newnode(LL val)
    {
        T[cont].init(val);
        cont++;
        return cont-1;
    }
    void rotate(int x,int d)//0 左旋     1右旋
    {
        int y=fa[x],z=fa[y];
        T[y].son[!d]=T[x].son[d],fa[T[x].son[d]]=y;
        fa[y]=x,T[x].son[d]=y,fa[x]=z;
        T[z].son[rs(z)==y]=x;
    }
    void splay(int x,int goal)//splay操作
    {
        if(x==0) return ;
        while(fa[x]!=goal)
        {
            int y=fa[x],z=fa[y];
            int rx=ls(y)==x,ry=ls(z)==y;
            if(z==goal) rotate(x,rx);
            else
            {
                if(rx==ry) rotate(y,ry);
                else rotate(x,rx);
                rotate(x,ry);
            }
        }
        if(goal==0) root=x;
    }
    void insert(int &x,LL val,int pre)//插入操作
    {
        if(x==0)
        {
            x=newnode(val);
            fa[x]=pre;
            splay(x,0);
            return ;
        }
        if(val<=T[x].val)
            insert(ls(x),val,x);
        else
            insert(rs(x),val,x);
    }
    void del(int k,LL val)//刪除值爲val 的節點
    {
        if(T[k].val==val)//找到節點
        {
            splay(k,0);//先將該節點旋轉到根節點
            int tmp=ls(k);
            if(tmp==0)//左節點爲空
            {
                fa[rs(k)]=0;
                root=rs(0)=rs(k);
                return ;
            }
            while(rs(tmp)!=0)//找到左子樹中最大的值的節點
            {
                tmp=rs(tmp);
            }
            splay(tmp,k);//將這個節點旋轉到根節點的下方
            root=tmp;//然後刪除根節點
            rs(tmp)=rs(k);
            fa[rs(k)]=tmp;
            fa[tmp]=0;
            rs(0)=tmp;
            return ;
        }
        if(val<T[k].val)//遞歸找節點
            return del(ls(k),val);
        else
            return del(rs(k),val);
    }
    LL query(int k,LL val,LL ans)//查詢最接近val值的節點並返回差值,ans代表到目前爲止最優的解
    {
        if(k==0)//找到最後了代表ans是這棵樹中最優的節點了
        {
            del(root,ans);
            return abs(ans-val);
        }
        if(T[k].val==val)//找到相同的節點
        {
            del(root,val);
            return 0;
        }
        if(abs(T[k].val-val)<abs(ans-val))//更新ans值
            ans=T[k].val;
        else if(abs(T[k].val-val)==abs(ans-val))
        {
            if(ans-val>0)
                ans=T[k].val;
        }
        if(val<T[k].val)
            return query(ls(k),val,ans);
        else
            return query(rs(k),val,ans);
    }
} ac;
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        ac.init();
        int flag=-1,sum=0;//flag標記這棵樹是什麼樹,0代表寵物 1代表領養者
        LL ans=0;
        while(n--)
        {
            cnt=0;
            int opt;
            LL val;
            scanf("%d%lld",&opt,&val);
            if(sum==0)//樹爲空
            {
                ac.root=0;
                flag=opt;
                ac.insert(ac.root,val,0);
                sum++;
            }
            else if(opt==flag)
            {
                ac.insert(ac.root,val,0);
                sum++;
            }
            else
            {
                ans=(ans+ac.query(ac.root,val,-inf))%mod;
                sum--;
            }
        }
        printf("%lld\n",ans);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章