zoukankan      html  css  js  c++  java
  • bzoj3223 Tyvj 1729 文艺平衡树

    3223: Tyvj 1729 文艺平衡树

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 5813  Solved: 3464
    [Submit][Status][Discuss]

    Description

     

    您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 

    Input

    第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2……n-1,n)  m表示翻转操作次数
    接下来m行每行两个数[l,r] 数据保证 1<=l<=r<=n 

     

    Output

     

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

     

    Sample Input

    5 3

    1 3

    1 3

    1 4

    Sample Output

    4 3 2 1 5

    HINT

    N,M<=100000

    Source

    平衡树

    分析:splay上的区间操作.

              一般的区间操作可以通过线段树解决,但是有一部分区间操作只能通过splay来解决.它在区间操作上和线段树有相通的地方:build,pushup,pushdown这些操作基本上都差不多,但是又有一些细微的差异.

              以这题为例,先提取区间,将l-1转到根节点,再将r+1转到根节点的右节点.那么根节点的右节点的左子树就是要求的[l,r]区间.为了能够方便的提取区间[1,n],加入两个哨兵元素0,n + 1,那么实际操作的就是l,r+2.

              翻转操作可以变成逐层翻转.就是将要翻转的区间在splay中的每一个点的左右子树交换,从上到下.为了提高效率,打个标记.那么实际上的翻转操作就都在pushdown中完成了.以后不管执行什么操作,若是从上到下,执行到x就要pushdown(x),最后pushup(x).

              和线段树的一些小区别:建树是[l,mid - 1],[mid + 1,r],中间的mid不存在了.这样做我个人认为是因为splay中每个点代表一个元素,而线段树每个点代表一个区间. 在pushup的时候要加上当前节点的值!

              最后中序遍历一遍就出来了,这里用到了一个原理:splay的中序遍历=原序列.

              几个注意点:1.pushup注意顺序!先子树,后父亲. 2.输出判断当前点是否是哨兵元素. 3.因为插入了哨兵元素,所以每个元素的位置都要往后挪1. 4.splay的最后不要轻易换根,因为这不是直接旋转到根节点,而是旋转到某个点的下面,要先判断旋转到的点是不是0,是的话才能换根.

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int maxn = 200010;
    
    int n,m,tot,root,sizee[maxn];
    
    struct node
    {
        int fa,left,right,v,tag;
    }e[maxn];
    
    void update(int x)
    {
        sizee[x] = 1;
        if (e[x].left != 0)
            sizee[x] += sizee[e[x].left];
        if (e[x].right != 0)
            sizee[x] += sizee[e[x].right];
    }
    
    void pushdown(int x)
    {
        if (!x)
            return;
        if (e[x].tag)
        {
            e[e[x].left].tag ^= 1;
            e[e[x].right].tag ^= 1;
            e[x].tag = 0;
            int t = e[x].left;
            e[x].left = e[x].right;
            e[x].right = t;
        }
    }
    
    int build(int l,int r) //感觉这个建树不仅赋予了每个点代表的值,还标记了左右区间.
    {
        if (l > r)
            return 0;
        int x = ++tot;
        int mid = (l + r) >> 1;
        e[x].fa = e[x].left = e[x].right = e[x].tag = 0;
        e[x].v = mid;
        sizee[x] = 1;
        e[x].left = build(l,mid - 1);
        e[x].right = build(mid + 1,r);
        e[e[x].left].fa = e[e[x].right].fa = x;
        update(x);
        return x;
    }
    
    void turnr(int x)
    {
        pushdown(x);
        int y = e[x].fa;
        int z = e[y].fa;
        e[y].left = e[x].right;
        if (e[x].right != 0)
            e[e[x].right].fa = y;
        e[x].fa = z;
        if (z != 0)
        {
            if (e[z].left == y)
                e[z].left = x;
            else
                e[z].right = x;
        }
        e[x].right = y;
        e[y].fa = x;
        update(x);
        update(y);
    }
    
    void turnl(int x)
    {
        pushdown(x);
        int y = e[x].fa;
        int z = e[y].fa;
        e[y].right = e[x].left;
        if (e[x].left != 0)
            e[e[x].left].fa = y;
        e[x].fa = z;
        if (z != 0)
        {
            if (e[z].left == y)
                e[z].left = x;
            else
                e[z].right = x;
        }
        e[x].left = y;
        e[y].fa = x;
        update(x);
        update(y);
    }
    
    void splay(int x,int yy)
    {
        if (yy == 0)
            root = x;
        while (e[x].fa != yy)
        {
            pushdown(x);
            int y = e[x].fa;
            int z = e[y].fa;
            if (z == yy || z == 0)
            {
                if (x == e[y].left)
                    turnr(x);
                else
                    turnl(x);
            }
            else
            {
                if (e[z].left == y && e[y].left == x)
                {
                    turnr(y);
                    turnr(x);
                }
                else
                {
                    if (e[z].right == y && e[y].right == x)
                    {
                        turnl(y);
                        turnl(x);
                    }
                    else
                    {
                        if (e[z].left == y && e[y].right == x)
                        {
                            turnl(x);
                            turnr(x);
                        }
                        else
                        {
                                turnr(x);
                                turnl(x);
                        }
                    }
                }
            }
        }
        if (yy == 0)
            root = x;
    }
    int find(int x,int k)
    {
        pushdown(x);
        if (k > sizee[e[x].left] + 1)
            return find(e[x].right,k - 1 - sizee[e[x].left]);
        if (k == sizee[e[x].left] + 1)
            return x;
        return find(e[x].left,k);
    }
    
    void dfs(int x)
    {
        pushdown(x);
        if (e[x].left)
            dfs(e[x].left);
        if (e[x].v >= 1 && e[x].v <= n)
            printf("%d ",e[x].v);
        if (e[x].right)
            dfs(e[x].right);
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        root = build(0,n + 1);
        for (int i = 1; i <= m; i++)
        {
            int l,r;
            scanf("%d%d",&l,&r);
            int p = find(root,l),q = find(root,r + 2);
            splay(p,0);
            splay(q,p);
            e[e[q].left].tag ^= 1;
            update(e[q].left);
            update(q);
            update(root);
        }
        dfs(root);
    
        return 0;
    }
  • 相关阅读:
    mouse_event模拟鼠标滚轮
    润乾报表配置技术路线
    建筑 物件 开心背单词 读句子,单词,字母,看图例, 翻译,看动画
    文字过渡动画,曲线过渡动画,,使用这个插件assign shape keys
    运动锻炼 开心背单词 读句子,单词,字母,看图例, 翻译,看动画,学英语,轻松背单词,简单背单词
    blender293 内置插件 精度绘画控件,PDT学习003,pdt tangents 切线
    日常用品 背单词 读句子 看图片 读单词 读字母 翻译, 看动画 学英语
    blender293 内置插件 精度绘画控件,PDT学习 precision drawing tools
    乔布斯 背单词 02 读句子 单词 字母 翻译,看动画 学英语 名言 我菜顾我在,我菜故我在,blender加python
    狐狸 和 乌鸦 英语 朗读句子 背单词
  • 原文地址:https://www.cnblogs.com/zbtrs/p/8270688.html
Copyright © 2011-2022 走看看