Luogu P3391 【模板】文艺平衡树(FHQ-Treap)

题意

给出一个长为$n$序列$[1,2,...,n]$,$m$次操作,每次指定一段区间$[l,r]$,将这段区间翻转,求最终序列

题解

虽然标题是$Splay$,但是我要用$FHQ\ Treap$,考虑先将$[l,r]$这段区间$split$出来($k$即为这段区间)

void split(int o, int k, int &l, int &r) {
    if(!o) { l = r = 0; return ; }
    if(siz[lc[o]] < k) l = o, split(rc[o], k - siz[lc[o]] - 1, rc[o], r);
    else r = o, split(lc[o], k, l, lc[o]);
    upt(o);
}//注意这里要按size来split

//写在main函数中
while(m--) {
    read(x), read(y);
    split(rt, y, l, r), split(l, x - 1, l, k);
    rev[k] ^= 1; rt = merge(merge(l, k), r);
}//x,y为操作的区间

然后再将这段区间打一个翻转标记(因为平衡树是可以中序遍历输出的吧...,$rev$为翻转标记)

每次涉及到某个节点时,将$rev$标记下放就好了

void pushdown(int o) {
    std::swap(lc[o], rc[o]);
    if(lc[o]) rev[lc[o]] ^= 1;
    if(rc[o]) rev[rc[o]] ^= 1;
    rev[o] = 0;
}

#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <algorithm>

template<typename T>
void read(T &x) {
    int flag = 1; x = 0; char ch = getchar();
    while(ch < ‘0‘ || ch > ‘9‘) { if(ch == ‘-‘) flag = -flag; ch = getchar(); }
    while(ch >= ‘0‘ && ch <= ‘9‘) x = x * 10 + ch - ‘0‘, ch = getchar(); x *= flag;
}

const int N = 1e5 + 10;
int n, m, lc[N], rc[N], siz[N], val[N], pri[N], rev[N], tot;

inline void upt(int o) { siz[o] = siz[lc[o]] + siz[rc[o]] + 1; }
inline int node(int x) { val[++tot] = x, pri[tot] = rand(), siz[tot] = 1; return tot;}
void pushdown(int o) {
    std::swap(lc[o], rc[o]);
    if(lc[o]) rev[lc[o]] ^= 1;
    if(rc[o]) rev[rc[o]] ^= 1;
    rev[o] = 0;
}
void split(int o, int k, int &l, int &r) {
    if(!o) { l = r = 0; return ; }
    if(rev[o]) pushdown(o);
    if(siz[lc[o]] < k) l = o, split(rc[o], k - siz[lc[o]] - 1, rc[o], r);
    else r = o, split(lc[o], k, l, lc[o]);
    upt(o);
}
int merge(int l, int r) {
    if(!l || !r) return l + r;
    if(pri[l] < pri[r]) { if(rev[l]) pushdown(l); rc[l] = merge(rc[l], r), upt(l); return l; }
    else { if(rev[r]) pushdown(r); lc[r] = merge(l, lc[r]), upt(r); return r; }
}
void print(int o) {
    if(!o) return ;
    if(rev[o]) pushdown(o);
    print(lc[o]), printf("%d ", val[o]), print(rc[o]);
}

int main () {
    read(n), read(m), srand((unsigned)time(NULL));
    int x, y, l, r, k, rt = 0;
    for(int i = 1; i <= n; ++i) rt = merge(rt, node(i));
    while(m--) {
        read(x), read(y);
        split(rt, y, l, r), split(l, x - 1, l, k);
        rev[k] ^= 1; rt = merge(merge(l, k), r);
    } print(rt);
    return 0;
}

原文地址:https://www.cnblogs.com/water-mi/p/10159803.html

时间: 2024-10-19 02:19:47

Luogu P3391 【模板】文艺平衡树(FHQ-Treap)的相关文章

模板 文艺平衡树

Splay模板,学完觉得比Treap简单,不过均摊的logn不能可持久化. 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define ls ch[0] 5 #define rs ch[1] 6 struct trnt{ 7 int ch[2]; 8 int chd; 9 int wgt; 10 int fa; 11 int v; 12 }tr[10000000]; 13 int root

[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

洛谷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

数组splay ------ luogu P3369 【模板】普通平衡树(Treap/SBT)

二次联通门 : luogu P3369 [模板]普通平衡树(Treap/SBT) #include <cstdio> #define Max 100005 #define Inline __attri\ bute__( ( optimize( "-O2" ) ) ) Inline void read (int &now) { now = 0; register char word = getchar (); bool temp = false; while (wor

替罪羊树 ------ luogu P3369 【模板】普通平衡树(Treap/SBT)

二次联通门 : luogu P3369 [模板]普通平衡树(Treap/SBT) 闲的没事,把各种平衡树都写写 比较比较... 下面是替罪羊树 #include <cstdio> #include <vector> #define Max_ 100010 #define Inline __attri\ bute__( ( optimize( "-O2" ) ) ) Inline void read (int &now) { register char w

红黑树 ------ luogu P3369 【模板】普通平衡树(Treap/SBT)

二次联通门 : luogu P3369 [模板]普通平衡树(Treap/SBT) 近几天闲来无事...就把各种平衡树都写了一下... 下面是红黑树(Red Black Tree) #include <cstdio> #define Max 100001 #define Red true #define Black false #define Inline __attri\ bute__( ( optimize( "-O2" ) ) ) Inline void read (i

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,

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,

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