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;
    }
  • 相关阅读:
    Liskov替换原则
    OCP开放封闭原则
    SRC单一职责原则
    什么是敏捷设计
    [WCF编程]13.并发:服务并发模式
    [WCF编程]12.事务:服务事务编程(下)
    [WCF编程]12.事务:服务事务编程(上)
    [WCF编程]12.事务:Transaction类
    [WCF编程]12.事务:事务传播
    [WCF编程]12.事务:事务协议与管理器
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/9770884.html
Copyright © 2011-2022 走看看