嘘~ 正在从服务器偷取页面 . . .

洛谷P3391 【模板】文艺平衡树 题解


洛谷P3391 【模板】文艺平衡树 题解

题目链接:P3391 【模板】文艺平衡树

题意

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

其中需要提供以下操作:翻转一个区间。

例如原有序序列是 \(5\ 4\ 3\ 2\ 1\),翻转区间是 \([2,4]\) 的话,结果是 \(5\ 2\ 3\ 4\ 1\)

输入格式

第一行两个正整数 \(n,m\),表示序列长度与操作个数。序列中第 \(i\) 项初始为 \(i\)

接下来 \(m\) 行,每行两个正整数 \(l,r\)​,表示翻转的区间。

输出格式

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

数据范围

对于 \(100\%\) 的数据,$1 n, m $,\(1 \le l \le r \le n\)

文艺平衡树其实蛮好理解的。它和平衡树的区别在于,维护的是每个权值的编号。

也就是说我们不关心权值(权值不就是输出用么),我们只关心他们编号的顺序。

那么翻转一个区间只需要把 \(l\) 的前驱 splay 到根节点,再把 \(r\) 的后继 splay 到 \(l\) 的儿子

这样 \(l\) 的左儿子的子树就是我们要的区间,每次像线段树一样打个标记,然后 push_down 就搞定啦。

对了,代码里的 kth 不是 k 大值的意思,是指它 splay 到根后左子树有 \(k-1\) 个节点。

时间复杂度 \(\mathcal{O}(n \log n)\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x, int y) { x < y ? x = y : 0; }
void down(int &x, int y) { x > y ? x = y : 0; }
#define N ((int)(1e5 + 15))

int n;
namespace Splay
{
    int tot, rt; 
    #define ls(at) t[at].ch[0]
    #define rs(at) t[at].ch[1]
    struct node { int ch[2], num, fa, cnt, sz, tag; } t[N];
    int New(int x, int f)
    {
        t[++tot] = {0, 0, x, f, 1, 1, 0};
        if(f) t[f].ch[x > t[f].num] = tot;
        return tot;
    }
    void push_up(int at) {
        t[at].sz = t[ls(at)].sz + t[rs(at)].sz + t[at].cnt;
    }
    void push_down(int at)
    {
        if(t[at].tag)
        {
            t[ls(at)].tag ^= 1; t[rs(at)].tag ^= 1;
            t[at].tag = 0; swap(ls(at), rs(at));
        }
    }
    void rotate(int x)
    {
        int y = t[x].fa, z = t[y].fa, k = rs(y) == x;
        t[z].ch[rs(z) == y] = x; t[x].fa = z;
        t[y].ch[k] = t[x].ch[k ^ 1]; t[t[x].ch[k ^ 1]].fa = y;
        t[x].ch[k ^ 1] = y; t[y].fa = x;
        push_up(y); push_up(x);
    }
    void splay(int x, int goal)
    {
        while(t[x].fa != goal)
        {
            int y = t[x].fa, z = t[y].fa;
            if(z != goal) ((rs(y) == x) ^ (rs(z) == y)) ? rotate(x) : rotate(y);
            rotate(x);
        }
        if(!goal) rt = x;
    }
    void insert(int x)
    {
        int at = rt, f = 0;
        while(at && x != t[at].num) {
            f = at; at = t[at].ch[x > t[at].num];
        }
        if(at) ++t[at].cnt; else at = New(x, f);
        splay(at, 0);
    }
    int kth(int x)
    {
        int at = rt; if(t[at].sz < x) return INF;
        while(1)
        {
            push_down(at); int p = ls(at);
            if(x > t[p].sz + t[at].cnt)
            {
                x -= t[p].sz + t[at].cnt;
                at = t[at].ch[1];
            }else
            {
                if(x <= t[p].sz) at = p;
                else return t[at].num;
            }
        }
    }
}using namespace Splay;
void work(int l, int r)
{
    l = kth(l); r = kth(r + 2);
    splay(l, 0); splay(r, l);
    t[ls(rs(rt))].tag ^= 1;
}
void output(int at)
{
    push_down(at);
    if(ls(at)) output(ls(at));
    if(t[at].num > 1 && t[at].num < n + 2) cout << t[at].num - 1 << ' ';
    if(rs(at)) output(rs(at));
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int q; cin >> n >> q;
    for(int i = 1; i <= n + 2; i++) insert(i);
    while(q--) { int l, r; cin >> l >> r; work(l, r); }
    return output(rt), 0;
}

参考文献

[1] https://www.luogu.com.cn/article/21wjl9jr


题外话

splay 全部忘掉啦!!!!!怎么办!!!!!


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录