zoukankan      html  css  js  c++  java
  • P3391 【模板】文艺平衡树(Splay)

    传送门

    Splay模板题

    考虑如何把一颗树翻转

    把它的左右儿子翻转,左右儿子的左右儿子翻转...直到每个节点都被翻转

    一颗树这样转后可以发现树的中序遍历也刚好左右翻转了

    所以可以用Splay维护,维护标记也不难,只要每次向下之前都先传一下标记就可以了

    注意此时Splay节点的大小关系是他们在序列的位置而不是值

    至于提取区间的操作就只要把 l-1 号节点搞到根,把 r+1 号节点搞到根的右儿子

    那么整个 l~r 的区间就在 r+1 号节点的左儿子上了

    一开始建树可以直接建一个完美的平衡树

    因为可能会访问到0号节点或n+1号节点,所以要增加两个虚节点防止越界

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    const int N=1e5+7;
    inline int read()
    {
        register int x=0,f=1;
        char ch=getchar();
        while(ch<'0'||ch>'9')
        {
            if(ch=='-') f=-1;
            ch=getchar();
        }
        while(ch>='0'&&ch<='9')
        {
            x=(x<<1)+(x<<3)+(ch^48);
            ch=getchar();
        }
        return x*f;
    }
    
    int n,m;
    
    int ch[N<<2][2],val[N<<2],fa[N<<2],sz[N<<2],rt;
    bool rev[N<<2];//翻转标记
    inline void pushdown(int &x)//下传标记
    {
        if(rev[x])
        {
            int l=ch[x][0],r=ch[x][1];
            if(l)
            {
                rev[l]^=1;
                swap(ch[l][0],ch[l][1]);
            }
            if(r)
            {
                rev[r]^=1;
                swap(ch[r][0],ch[r][1]);
            }
            rev[x]=0;
        }
    }
    inline void pushup(int &x) { sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1; }//更新节点
    inline void rotate(int x,int &k)//伸展
    {
        int y=fa[x],z=fa[y],d=(ch[y][1]==x);
        if(y==k) k=x;
        else ch[z][(ch[z][1]==y)]=x;
        fa[x]=z; fa[y]=x; fa[ch[x][d^1]]=y;
        ch[y][d]=ch[x][d^1]; ch[x][d^1]=y;
        pushup(y); pushup(x);
    }
    inline void splay(int x,int &k)
    {
        while(x!=k)
        {
            int y=fa[x],z=fa[y];
            if(y!=k)
            {
                if( (ch[y][1]==x)^(ch[z][1]==y) ) rotate(x,k);
                else rotate(y,k);
            }
            rotate(x,k);
        }
    }
    void build(int l,int r,int f)//建树
    {
        int mid=l+r>>1;
        if(mid>l) build(l,mid-1,mid);
        if(mid<r) build(mid+1,r,mid);
        fa[mid]=f; ch[f][mid>f]=mid;
        pushup(mid);
    }
    inline int find(int k)//找到排名第k的数
    {
        int now=rt;
        while(233)
        {
            pushdown(now);//每次向下之前先下传标记
            if(ch[now][0]&&k<=sz[ch[now][0]]) { now=ch[now][0]; continue; }
            if(k>sz[ch[now][0]]+1)
            {
                k-=sz[ch[now][0]]+1;
                now=ch[now][1];
                continue;
            }
            return now;
        }
    }
    inline void rever(int l,int r)//翻转
    {
        int now=find(r+2);//注意r+2,因为有虚节点
        splay(find(l/*注意l不用减1因为有虚节点*/),rt); splay(now,ch[rt][1]);
        now=ch[now][0];
        swap(ch[now][0],ch[now][1]); rev[now]^=1;//翻转并打上标记
    }
    void print(int x)//中序遍历并输出
    {
        pushdown(x);//先传标记再向下
        if(ch[x][0]) print(ch[x][0]);
        if(val[x]>1&&val[x]<n+2)printf("%d ",val[x]-1);
        if(ch[x][1]) print(ch[x][1]);
    }
    
    int main()
    {
        n=read(); m=read();
        for(int i=1;i<=n+2;i++) val[i]=i;//新增一个最大和最小的虚节点
        rt=(1+n+2)>>1;
        build(1,n+2,0);
        int a,b;
        for(int i=1;i<=m;i++)
        {
            a=read(); b=read();
            rever(a,b);
        }
        print(rt);
        return 0;
    }
  • 相关阅读:
    Security and Cryptography in Python
    Security and Cryptography in Python
    Security and Cryptography in Python
    Security and Cryptography in Python
    Security and Cryptography in Python
    Security and Cryptography in Python
    Security and Cryptography in Python
    《EffectiveJava中文第二版》 高清PDF下载
    《MoreEffectiveC++中文版》 pdf 下载
    《啊哈c语言》 高清 PDF 下载
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/9770884.html
Copyright © 2011-2022 走看看