[HNOI2017]单旋

题目大意

你需要维护一个spaly,这个数据结构在将节点旋转到根只会一直单旋自己。
q 个操作,你需要对每一个操作都输出其时间代价,操作有如下几种:
 插入操作:插入一个关键字为 x 的点,插入方法是正常的二叉查找树的插入,时间代价是插入位置的深度。(注意:不要因为平时打的splay想当然地认为这个操作在执行完之后会将插入点旋转到根)
 单旋最小值:将spaly中的最小值单旋到根,代价为单旋之前该节点的深度。
  单旋最大值:将spaly中的最大值单旋到根,代价为单旋之前该节点的深度。
  单旋删除最小值:在单旋最小值的基础上,删掉根节点(显然没有左子树),代价同单旋最小值。
  单旋删除最大值:在单旋最大值的基础上,删掉根节点(显然没有右子树),代价同单旋最大值。
保证所有关键字互不相同。

1q105


题目分析

考虑使用link cut tree来维护树的形态,同时记录一下每个点的真实左右儿子和父亲。使用set可以很快地查询一个点的前驱和后继。插入时显然是插在前驱和后继两个点中深度较大的那个下面。单旋操作的话可以发现它其实只影响了很少几个点,其它点都不变,就瞎link一下cut一下就好了。深度的话可以直接用lct操作求。
时间复杂度 O(nlogn)


代码实现

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cctype>
#include <stack>
#include <set>

using namespace std;

int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    return x*f;
}

int buf[30];

void write(int x)
{
    if (x<0) putchar('-'),x=-x;
    for (;x;x/=10) buf[++buf[0]]=x%10;
    if (!buf[0]) buf[++buf[0]]=0;
    for (;buf[0];putchar('0'+buf[buf[0]--]));
}

const int N=100050;

int val[N];

struct cmp{bool operator()(const int& x,const int& y)const{return val[x]<val[y];}};
set<int,cmp> BST;
int q,tot;

namespace link_cut_tree
{
    int fa[N],size[N],par[N],Fa[N],left[N],right[N];
    bool mark[N];
    int son[N][2];
    stack<int> st;
    int root;

    bool side(int x){return son[fa[x]][1]==x;}

    void init(){size[0]=fa[0]=par[0]=0;}

    void update(int x){size[x]=size[son[x][0]]+size[son[x][1]]+1;}

    void R(int x){swap(son[x][0],son[x][1]),mark[x]^=1;}

    void clear(int x)
    {
        if (mark[x])
        {
            if (son[x][0]) R(son[x][0]);
            if (son[x][1]) R(son[x][1]);
            mark[x]=0;
        }
    }

    void rotate(int x)
    {
        int y=fa[x];bool s=side(x);
        if (fa[y]) son[fa[y]][side(y)]=x;
        if (son[x][s^1]) fa[son[x][s^1]]=y;
        son[y][s]=son[x][s^1],son[x][s^1]=y;
        fa[x]=fa[y],fa[y]=x;
        if (par[y]) par[x]=par[y],par[y]=0;
        update(y),update(x);
    }

    void pushdown(int x,int y)
    {
        for (;x!=y;st.push(x),x=fa[x]);
        for (;!st.empty();clear(st.top()),st.pop());
    }

    void splay(int x,int y)
    {
        for (pushdown(x,y);fa[x]!=y;rotate(x))
            if (fa[fa[x]]!=y)
                if (side(x)==side(fa[x])) rotate(fa[x]);
                else rotate(x);
    }

    int access(int x)
    {
        int nxt=0;
        for (;x;update(nxt=x),x=par[x])
        {
            splay(x,0);
            if (son[x][1]) par[son[x][1]]=x,fa[son[x][1]]=0;
            if (nxt) par[nxt]=0,fa[nxt]=x;
            son[x][1]=nxt;
        }
        return nxt;
    }

    void makeroot(int x){R(access(x));}

    void link(int x,int y)
    {
        makeroot(x),access(x),splay(x,0);
        par[x]=y,access(x);
    }

    void cut(int x,int y)
    {
        makeroot(x),access(y),splay(y,0);
        fa[x]=par[y]=son[y][0]=0,update(y);
    }

    int add(int x)
    {
        val[++tot]=x,x=tot;
        set<int,cmp>::iterator it=BST.insert(tot).first;
        if (!root) update(root=x);
        else
        {
            int p=0,q=0,d1=0,d2=0;
            if (it!=BST.begin()) p=*(--it),makeroot(root),access(p),splay(p,0),d1=size[p],++it;
            ++it;
            if (it!=BST.end()) q=*it,makeroot(root),access(q),splay(q,0),d2=size[q];
            --it;
            if (d1>d2) right[p]=x,Fa[x]=p,update(x),link(p,x);
            else left[q]=x,Fa[x]=q,update(x),link(q,x);
        }
        return makeroot(root),access(x),splay(x,0),size[x];
    }

    int getmin(bool del)
    {
        int x=*BST.begin(),ret,y;
        if (del) BST.erase(x);
        makeroot(root),access(x),splay(x,0),ret=size[x],y=right[x];
        if (x==root)
        {
            if (del&&y) Fa[y]=0,cut(x,y),root=y;
        }
        else
        {
            cut(x,Fa[x]),left[Fa[y]]=y,update(x);
            if (y) cut(x,y),Fa[y]=Fa[x],left[Fa[y]]=y,link(y,Fa[x]);
            Fa[x]=0;
            if (!del)
            {
                splay(root,0),Fa[root]=x,right[x]=root,update(root);
                link(x,root),root=x;
            }
        }
        return ret;
    }

    int getmax(bool del)
    {
        int x=*BST.rbegin(),ret,y;
        if (del) BST.erase(x);
        makeroot(root),access(x),splay(x,0),ret=size[x],y=left[x];
        if (x==root)
        {
            if (del&&y) Fa[y]=0,cut(x,y),root=y;
        }
        else
        {
            cut(x,Fa[x]),right[Fa[y]]=y,update(x);
            if (y) cut(x,y),Fa[y]=Fa[x],right[Fa[y]]=y,link(y,Fa[x]);
            Fa[x]=0;
            if (!del)
            {
                splay(root,0),Fa[root]=x,left[x]=root,update(root);
                link(x,root),root=x;
            }
        }
        return ret;
    }
};

int main()
{
    freopen("splay.in","r",stdin),freopen("splay.out","w",stdout);
    for (q=read();q--;)
    {
        int opt=read();
        if (opt==1) write(link_cut_tree::add(read())),putchar('\n');
        else
        {
            --opt;
            if (opt&1) write(link_cut_tree::getmin(opt==3)),putchar('\n');
            else write(link_cut_tree::getmax(opt==4)),putchar('\n');
        }
    }
    fclose(stdin),fclose(stdout);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值