• 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;
    }
  • 相关阅读:
    SQLyog连接mysql8,报错1251
    Oracle日期函数
    git 回退
    git新建分支并指定拉去远程分支
    git创建分支并拉去远端分支代码
    git创建空白分支
    Maven 本地仓库明明有jar包,pom文件还是报错解决办法(Missing artifact...jar)
    SqlHelper类
    ADO.NET中的模型及对象
    MVC过滤器---异常处理过滤器
  • 原文地址:https://www.cnblogs.com/zbtrs/p/8270688.html
Copyright © 2020-2023  润新知