luogu P3391 文艺平衡树

二次联通门 : luogu P3391 文艺平衡树

/*
    luogu 3391 文艺平衡树

    splay 区间翻转 

    每次翻转区间[l,r]都是把l旋到根节点上, r旋到根节点的右节点上
    那么要修改的区间就是根节点的右孩子的左子树
    然后打标记。。
    之后每次查到时就下放标记 

    原区间为1 ~ N
    增加两个哨兵节点 0 和 N + 1, 避免翻转1~N的区间产生麻烦 

*/
#include <cstdio>

#define Max 200009

inline int swap (int &a, int &b)
{
    int now = a;
    a = b;
    b = now;
}

inline void read (int &now)
{
    now = 0;
    register char word = getchar ();
    while (word < ‘0‘ || word > ‘9‘)
        word = getchar ();
    while (word >= ‘0‘ && word <= ‘9‘)
    {
        now = now * 10 + word - ‘0‘;
        word = getchar ();
    }
}

int value[Max];
int N, M;

class Splay_Tree_Type
{
    private :

        struct Splay_Tree_Date
        {
            int size;
            int key;
            int father;
            int child[2];
            int Flandre;
        }
        tree[Max]; 

        inline int Get_Son (int now)
        {
            return tree[tree[now].father].child[1] == now;
        }

        inline void Update (int now)
        {
            tree[now].size = 1;
            if (tree[now].child[0])
                tree[now].size += tree[tree[now].child[0]].size;
            if (tree[now].child[1])
                tree[now].size += tree[tree[now].child[1]].size;
        }

        int Root;
        int Answer[Max];
        int Count;

        inline void Down (int now)
        {
            tree[now].Flandre = 0;
            swap (tree[now].child[0], tree[now].child[1]);
            if (tree[now].child[0])
                tree[tree[now].child[0]].Flandre ^= 1;
            if (tree[now].child[1])
                tree[tree[now].child[1]].Flandre ^= 1;
        }

        inline void Rotate (int now)
        {
            int father = tree[now].father;
            int Grand = tree[father].father;
            int pos = Get_Son (now);
            if (tree[father].Flandre && father) // 注意这里标记下放的次序
                Down (father);
            if (tree[now].Flandre && now)
                Down (now);
            tree[father].child[pos] = tree[now].child[pos ^ 1];
            tree[tree[father].child[pos]].father = father;
            tree[now].child[pos ^ 1] =  father;
            tree[father].father = now;
            tree[now].father = Grand;
            if (Grand)
                tree[Grand].child[tree[Grand].child[1] == father] = now;
            Update (father);
            Update (now);
        }

        void Get_Answer (int now) // 把整棵树进行中序遍历后的序列就是答案
        {
            if (tree[now].Flandre)
                Down (now);
            if (tree[now].child[0])
                Get_Answer (tree[now].child[0]);
            Answer[++Count] = tree[now].key;
            if (tree[now].child[1])
                Get_Answer (tree[now].child[1]);
        }

        int Build (int l, int r, int father)
        {
            int now = l + r >> 1;
            tree[now].father = father;
            tree[now].key = value[now];
            if (now > l)
                tree[now].child[0] = Build (l, now - 1, now);
            if (now < r)
                tree[now].child[1] = Build (now + 1, r, now);
            Update (now);
            return now;
        }

    public :

        void Prepare ()
        {
            Root = 1;
            Root = Build(1, N + 2, 0);
        }

        void Splay (int now, int to)
        {
            for (int father; (father = tree[now].father) != to; Rotate (now))
                if (tree[father].father != to)
                    Rotate (Get_Son (now) == Get_Son (father) ? father : now);
            if (!to)
                Root = now;
        }

        int Get_Pos (int x)
        {
            int now = Root;
            for (; ; )
            {
                if (tree[now].Flandre)
                    Down (now);
                if (x <= tree[tree[now].child[0]].size)
                    now = tree[now].child[0];
                else
                {
                    x -= tree[tree[now].child[0]].size + 1;
                    if (!x)
                        return now;
                    now = tree[now].child[1];
                }
            }
        }

        inline void Print ()
        {
            Get_Answer (Root);
            for (int i = 1; i <= N; i++)
                printf ("%d ", Answer[i + 1]);
        }

        inline void Hit_flag ()
        {
            tree[tree[tree[Root].child[1]].child[0]].Flandre ^= 1;
        }
};

Splay_Tree_Type Make;

int main (int argc, char *argv[])
{
    read (N);
    read (M); // 0号点和 N+1 号点代表是哨兵节点
    for (int i = 1; i <= N + 2; i++) // 预先处理出值
        value[i] = i - 1;
    int l, r;
    Make.Prepare ();  // splay建树, 要建满二叉树
    for (; M--; )
    {
        read (l);
        read (r);
        r += 2;
        l = Make.Get_Pos (l); // 找到在树中对应的位置
        r = Make.Get_Pos (r);
        Make.Splay (l, 0);
        Make.Splay (r, l);
        Make.Hit_flag ();  // 打上标记
    }
    Make.Print ();
    return 0;
}
时间: 2024-10-10 04:45:35

luogu P3391 文艺平衡树的相关文章

[Splay]luogu P3391 文艺平衡树

题目描述 https://www.luogu.org/problemnew/show/P3391 分析 文艺平衡树 一道大家熟知的splay区间翻转模板题 基于splay的区间翻转,我们要做的只有这些: 1.像线段树一样打翻转标记,不过由于翻转是可以抵消的,所以可以采取位运算节省时间 2.翻转只需要逐层翻转即可,正确性已有论证 3.对于区间如何确定的问题,我们只需要将l-1节点旋至根,r+1节点旋至根下即可 4.对于1~x或x~n区间的操作,我们还需要0和n+1这两个哨兵节点 #include

【Luogu】P3391文艺平衡树(Splay)

题目链接 ddosvoid和自为风月马前卒教了我这道题 他们好强啊 如果我们要反转区间[l,r] 我们首先把l的前驱旋转到根节点 再把r的后继旋转到根节点的右儿子 那么此时根节点的右儿子的左儿子所代表的就是区间l,r 具体为啥不知道 然后可以给splay的节点打标记,就像线段树一样 inline void pushdown(int x){ if(!tree[x].tag) return; swap(tree[x].e[0],tree[x].e[1]); tree[tree[x].e[0]].ta

P3391 文艺平衡树

hh 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 输入输出格式 输入格式: 第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2……n-1,n) m表示翻转操作次数 接下来m行每行两个数[l,r] 数据保证 1<=l<=r<=n 输出格式: 输出一行n个数字,表示原始序列经过m次变换后的结果 输入输出样例 输入样例#1: 5 3

P3391 【模板】文艺平衡树(Splay)新板子

P3391 [模板]文艺平衡树(Splay) 题目背景 这是一道经典的Splay模板题——文艺平衡树. 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 输入输出格式 输入格式: 第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2, \cdots n-1,n)(1,2,?n−1,n) m表示翻转操作次数 接下来m行每行两个数 [l,r][l,

AC日记——文艺平衡树 洛谷 P3391

文艺平衡树 思路: splay翻转操作模板: 虚拟最左最右端点,然后每次都把l翻转到root,r+2翻转到root的右节点: 然后在r+2的左节点上打标记: 标记需要在旋转,rank,print时下放: 建树需要用完全平衡二叉树: 来,上代码: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define m

P3391 【模板】文艺平衡树(Splay)

题目背景 这是一道经典的Splay模板题--文艺平衡树. 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 输入输出格式 输入格式: 第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2, \cdots n-1,n)(1,2,?n?1,n) m表示翻转操作次数 接下来m行每行两个数 [l,r][l,r] 数据保证 1 \leq l \leq r

洛谷P3391 【模板】文艺平衡树(Splay)

题目背景 这是一道经典的Splay模板题——文艺平衡树. 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 输入输出格式 输入格式: 第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2, \cdots n-1,n)(1,2,?n−1,n) m表示翻转操作次数 接下来m行每行两个数 [l,r][l,r] 数据保证 1 \leq l \leq r

洛谷P3391 【模板】文艺平衡树(Splay)(FHQ Treap)

题目背景 这是一道经典的Splay模板题——文艺平衡树. 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 输入输出格式 输入格式: 第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2, \cdots n-1,n)(1,2,?n−1,n) m表示翻转操作次数 接下来m行每行两个数 [l,r][l,r] 数据保证 1 \leq l \leq r

洛谷 P3391 【模板】文艺平衡树(Splay)

题目背景 这是一道经典的Splay模板题--文艺平衡树. 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 输入格式: 第一行为n,m n表示初始序列有n个数,这个序列依次是 (1,2,?n?1,n) m表示翻转操作次数 接下来m行每行两个数 [l,r][l,r] 数据保证 1≤l≤r≤n 输出格式: 输出一行n个数字,表示原始序列经过m次变换后的结果