BZOJ 4825: [AH2017/HNOI2017]单旋 LCT set

title

BZOJ 4825

LUOGU 3721

题面巨长,不想贴了,不然这篇 \(blog\) 就都成题面了

analysis

\(Orz\) yyb

这题的题目让你维护一个 \(splay\) ,正解就肯定不是 \(splay\) 了,要不然就有点不正常了(刚才去翻了一下洛谷题解,好像真的有大佬是写了 \(splay\) 的,有点打脸)。

反正我选择用 \(LCT\) 来维护这棵 \(splay\) 了(默认 \(LCT\) 都会写了)。

下面说下五个操作:

  1. 第一个操作,一个新建入的节点要么接在前驱的右儿子,要么接在后继的左儿子,那么,搞一个 \(set\) 求一求前驱后继,很容易证明前驱的右儿子和后继的左儿子一定有且仅有一个为空,直接接上去就行了。

  2. 剩下的四个操作,最大/最小值旋到根,就是把它的儿子给父亲,然后 \(root\) 直接变成它的儿子,它变成 \(root\) ,于是每次的操作之和两个点有关,在 \(LCT\) 中维护点在 \(spaly\) 上的父子关系。(在删除操作的时候,要先判断这个点已经是 \(root\) 的情况的)

code

写的巨长,\(258\)

#include<bits/stdc++.h>

const int maxn=2e5+10;

namespace IO
{
    char buf[1<<15],*fs,*ft;
    inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
    template<typename T>inline void read(T &x)
    {
        x=0;
        T f=1, ch=getchar();
        while (!isdigit(ch) && ch^'-') ch=getchar();
        if (ch=='-') f=-1, ch=getchar();
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }

    char Out[1<<24],*fe=Out;
    inline void flush() { fwrite(Out,1,fe-Out,stdout); fe=Out; }
    template<typename T>inline void write(T x,char str)
    {
        if (!x) *fe++=48;
        if (x<0) *fe++='-', x=-x;
        T num=0, ch[20];
        while (x) ch[++num]=x%10+48, x/=10;
        while (num) *fe++=ch[num--];
        *fe++=str;
    }
}

using IO::read;
using IO::write;

namespace LCT
{
    int ch[maxn][2];
    typedef int iarr[maxn];
    iarr fa,siz,Stack;
    bool r[maxn];
    inline bool nroot(int x)
    {
        return ch[fa[x]][0]==x || ch[fa[x]][1]==x;
    }

    inline void pushup(int x)
    {
        siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;
    }

    inline void rever(int x)
    {
        std::swap(ch[x][0],ch[x][1]);
        r[x]^=1;
    }

    inline void pushdown(int x)
    {
        if (r[x])
        {
            if (ch[x][0]) rever(ch[x][0]);
            if (ch[x][1]) rever(ch[x][1]);
            r[x]=0;
        }
    }

    inline void rotate(int x)//一次旋转
    {
        int y=fa[x],z=fa[y],k=ch[y][1]==x,w=ch[x][!k];
        if (nroot(y)) ch[z][ch[z][1]==y]=x;
        ch[x][!k]=y,ch[y][k]=w;//额外注意if(nroot(y))语句,此处不判断会引起致命错误(与普通Splay的区别2)
        if (w) fa[w]=y;
        fa[y]=x,fa[x]=z;
        pushup(y);
    }

    inline void splay(int x)
    {
        int y=x,z=0;
        Stack[++z]=y;
        while (nroot(y)) Stack[++z]=y=fa[y];
        while (z) pushdown(Stack[z--]);
        while (nroot(x))
        {
            y=fa[x], z=fa[y];
            if (nroot(y)) rotate((ch[y][0]==x)^(ch[z][0]==y) ? x : y);
            rotate(x);
        }
        pushup(x);
    }

    inline void access(int x)
    {
        for (int y=0; x; y=x, x=fa[x]) splay(x), ch[x][1]=y, pushup(x);
    }

    inline void makeroot(int x)
    {
        access(x);splay(x);rever(x);
    }

    inline int findroot(int x)
    {
        access(x);splay(x);
        while (ch[x][0]) pushdown(x), x=ch[x][0];
        splay(x);
        return x;
    }

    inline void split(int x,int y)
    {
        makeroot(x);
        access(y), splay(y);
    }

    inline void link(int x,int y)
    {
        makeroot(x);
        if (findroot(y)^x) fa[x]=y;
    }

    inline void cut(int x,int y)
    {
        makeroot(x);
        if (findroot(y)==x && fa[y]==x && !ch[y][0])
        {
            fa[y]=ch[x][1]=0;
            pushup(x);
        }
    }
}

using LCT::siz;
using LCT::split;
using LCT::link;
using LCT::cut;

int tot,root;
int ls[maxn],rs[maxn],fa[maxn];
std::map<int,int>M;
std::set<int>s;
inline void insert(int x)
{
    int now=++tot;
    M[x]=now;
    if (s.empty())
    {
        s.insert(x);
        root=now;
        write(1,'\n');
        return ;
    }
    std::set<int>::iterator it=s.upper_bound(x);
    if (it==s.end() || ls[M[*it]])
    {
        --it;
        int k=M[*it];
        link(now,k), rs[k]=now, fa[now]=k;
    }
    else
    {
        int k=M[*it];
        link(now,k), ls[k]=now,fa[now]=k;
    }
    s.insert(x);
    split(now,root);
    write(siz[root],'\n');
}

inline void Splay_min()
{
    int x=M[*s.begin()];
    if (root==x) { write(1,'\n'); return ; }
    split(x,root);
    write(siz[root],'\n');
    cut(x,fa[x]);
    if (rs[x]) cut(x,rs[x]);
    link(x,root);
    if (rs[x]) link(fa[x],rs[x]);
    ls[fa[x]]=rs[x];
    if (rs[x]) fa[rs[x]]=fa[x];
    fa[x]=0, fa[root]=x, rs[x]=root;
    root=x;
}

inline void Splay_max()
{
    int x=M[*--s.end()];
    if (root==x) { write(1,'\n'); return ; }
    split(x,root);
    write(siz[root],'\n');
    cut(x,fa[x]);
    if (ls[x]) cut(x,ls[x]);
    link(x,root);
    if (ls[x]) link(ls[x],fa[x]);
    rs[fa[x]]=ls[x];
    if (ls[x]) fa[ls[x]]=fa[x];
    fa[x]=0, fa[root]=x, ls[x]=root;
    root=x;
}

inline void Del_min()
{
    int x=M[*s.begin()];
    if (root==x)
    {
        write(1,'\n');
        if (rs[x]) cut(x,rs[x]);
        fa[rs[x]]=0, root=rs[x];
        s.erase(s.begin());
        return ;
    }
    split(x,root);
    write(siz[root],'\n');
    cut(x,fa[x]);
    if (rs[x]) cut(x,rs[x]);
    if (rs[x]) link(fa[x],rs[x]);
    ls[fa[x]]=rs[x];
    if (rs[x]) fa[rs[x]]=fa[x];
    s.erase(s.begin());
}

inline void Del_max()
{
    int x=M[*--s.end()];
    if (root==x)
    {
        write(1,'\n');
        if (ls[x]) cut(x,ls[x]);
        fa[ls[x]]=0, root=ls[x];
        s.erase(--s.end());
        return ;
    }
    split(x,root);
    write(siz[root],'\n');
    cut(x,fa[x]);
    if (ls[x]) cut(x,ls[x]);
    if (ls[x]) link(ls[x],fa[x]);
    rs[fa[x]]=ls[x];
    if (ls[x]) fa[ls[x]]=fa[x];
    s.erase(--s.end());
}

int main()
{
    int n;read(n);
    while (n--)
    {
        int opt,x;read(opt);
        if (opt==1) read(x), insert(x);
        else if (opt==2) Splay_min();
        else if (opt==3) Splay_max();
        else if (opt==4) Del_min();
        else Del_max();
    }
    IO::flush();
    return 0;
}

转载于:https://www.cnblogs.com/G-hsm/p/11447867.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值