zoukankan      html  css  js  c++  java
  • 洛谷P3391 文艺平衡树---Splay

    题目链接:https://www.luogu.com.cn/problem/P3391

    时间限制1.00s
    内存限制125.00MB
     

    题目描述

    您需要写一种数据结构(可参考题目标题),来维护一个有序数列。

    其中需要提供以下操作:翻转一个区间,例如原有序序列是 5 4 3 2 1,翻转区间是 [2,4] 的话,结果是 5 2 3 4 1。

    输入格式

    第一行两个正整数 n,m表示序列长度与操作个数。序列中第 i 项初始为 i
    接下来 m 行,每行两个正整数 l,r,表示翻转的区间。

    输出格式

    输出一行 n 个正整数,表示原始序列经过 m 次变换后的结果。

    输入输出样例

    输入
    5 3
    1 3
    1 3
    1 4
    输出 
    4 3 2 1 5

    说明/提示

    【数据范围】
    对于 100% 的数据,1n,m100000,1lrn。


    emmm,没什么好说的,上来就是个Splay板子,然后想想,如果按照它原来的顺序建树的话就是一条只有右儿子的链。我们考虑使用线段树的方式,一直找中点,就可以使得它的深度接近完美:

    int build(int l,int r,int fa)
    {
        if (l>r) return 0;
        int mid=(l+r)>>1;
        int rt=++num;
        tree[rt].f=fa;
        tree[rt].son[0]=tree[rt].son[1]=0;
        tree[rt].cnt++;
        tree[rt].val=a[mid];
        tree[rt].size++;
        tree[rt].son[0]=build(l,mid-1,rt);
        tree[rt].son[1]=build(mid+1,r,rt);
        update(rt);
        return rt;
    }

    然后就开始翻转了。。。实际上翻转操作的话我们只需要将l-1伸展到根结点,r+1伸展到根结点的右儿子,那么剩下的就是区间[l,r]了:

    我们找到l~r的第一结点打上标记后下传,对于有标记的我们直接翻转左右儿子就达到翻转的效果了。至于l~r的第一结点位置就很好找,它是根结点的右儿子的左儿子。

    于是我们的翻转操作就可以完美写出来了:

    void reverse(int l,int r)
    {
        l--,r++;
        l=find(l);r=find(r);
        splay(l,0);//将l-1伸展到根节点
        splay(r,l);//将r+1伸展到根节点后继
        int pos=tree[root].son[1];
        pos=tree[pos].son[0];//找到l-r所在的位置
        tree[pos].tag^=1;//打旋转标记
    }

    然后套个splay的板子,find(x)是指找到x所在树中的位置,类似于权值线段树的写法:

    int find(int x)//找到x所在的结点
    {
        int rt=root;
        while (1){
            push_down(rt);
            if (x<=tree[tree[rt].son[0]].size) rt=tree[rt].son[0];
            else {
                x-=tree[tree[rt].son[0]].size+1;
                if (!x) return rt;
                rt=tree[rt].son[1];
            }
        }
    }

    以下是AC代码:

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    
    const int mac=1e5+10;
    
    struct Tree
    {
        int son[2],cnt,val,f,size,tag;
    }tree[mac];
    int root,num,a[mac],_min,_max;
    
    void update(int rt)
    {
        if (!rt) return;
        tree[rt].size=tree[rt].cnt;
        if (tree[rt].son[0]) tree[rt].size+=tree[tree[rt].son[0]].size;
        if (tree[rt].son[1]) tree[rt].size+=tree[tree[rt].son[1]].size;
    }
    
    int build(int l,int r,int fa)
    {
        if (l>r) return 0;
        int mid=(l+r)>>1;
        int rt=++num;
        tree[rt].f=fa;
        tree[rt].son[0]=tree[rt].son[1]=0;
        tree[rt].cnt++;
        tree[rt].val=a[mid];
        tree[rt].size++;
        tree[rt].son[0]=build(l,mid-1,rt);
        tree[rt].son[1]=build(mid+1,r,rt);
        update(rt);
        return rt;
    }
    
    void push_down(int rt)//传下旋转标记
    {
        if ((!rt) || (!tree[rt].tag)) return;
        tree[tree[rt].son[0]].tag^=1;
        tree[tree[rt].son[1]].tag^=1;
        swap(tree[rt].son[0],tree[rt].son[1]);
        tree[rt].tag=0;
    }
    
    int find(int x)//找到x所在的结点
    {
        int rt=root;
        while (1){
            push_down(rt);
            if (x<=tree[tree[rt].son[0]].size) rt=tree[rt].son[0];
            else {
                x-=tree[tree[rt].son[0]].size+1;
                if (!x) return rt;
                rt=tree[rt].son[1];
            }
        }
    }
    
    bool which(int x){return x==tree[tree[x].f].son[1];}//判断x是左右儿子的哪一个
    
    void rotate(int rt)
    {
        int frt=tree[rt].f;
        int ffrt=tree[frt].f;
        push_down(rt);push_down(frt);
        bool w=which(rt);
        tree[frt].son[w]=tree[rt].son[w^1];
        tree[tree[frt].son[w]].f=frt;
        tree[frt].f=rt;
        tree[rt].f=ffrt;
        tree[rt].son[w^1]=frt;
        if (ffrt) tree[ffrt].son[tree[ffrt].son[1]==frt]=rt;
        update(frt);
    }
    
    void splay(int rt,int goal)//将rt伸展为goal的儿子
    {
        for (int i;(i=tree[rt].f)!=goal;rotate(rt)){
            if (tree[i].f!=goal)
                rotate(which(rt)==which(i)?i:rt);
        }
        if (goal==0) root=rt;
    }
    
    void reverse(int l,int r)
    {
        l--,r++;
        l=find(l);r=find(r);
        splay(l,0);//将l-1伸展到根节点
        splay(r,l);//将r+1伸展到根节点后继
        int pos=tree[root].son[1];
        pos=tree[pos].son[0];//找到l-r所在的位置
        tree[pos].tag^=1;//打旋转标记
    }
    
    void mid_dfs(int rt)//中序遍历输出
    {
        push_down(rt);
        if (tree[rt].son[0]) mid_dfs(tree[rt].son[0]);
        if (tree[rt].val!=_max && tree[rt].val!=_min)
            printf("%d ",tree[rt].val);
        if (tree[rt].son[1]) mid_dfs(tree[rt].son[1]);
    }
    
    int main()
    {
        int n,m;
        scanf ("%d%d",&n,&m);
        a[1]=0;a[n+2]=n+1;
        _max=n+1,_min=0;
        for (int i=1; i<=n; i++) a[i+1]=i;
        root=build(1,n+2,0);
        while (m--){
            int l,r;
            scanf("%d%d",&l,&r);
            reverse(l+1,r+1);
        }
        mid_dfs(root);
        printf("
    ");
        return 0;
    }
    View Code
    路漫漫兮
  • 相关阅读:
    我的周记17——“世界再美,也美不过我一日三餐,心情再坏,也坏不过我没心没肺”
    【测试-移动端】 聊聊手机端的测试
    Java基础篇---多线程
    BIO和NIO实现文件复制
    IO操作-BIO
    js数组去重
    js json对象操作
    23种设计模式,每天一种设计模式(2)
    23种设计模式,每天一种设计模式
    .net画二叉树
  • 原文地址:https://www.cnblogs.com/lonely-wind-/p/12249218.html
Copyright © 2011-2022 走看看