[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 <iostream>
#include <cstdio>
using namespace std;
const int N=1e5+10;
struct Node {
    int sz,val,f,c[2];
    bool rev;
}t[N];
int cnt,rt;
int n,m;

void Update(int x) {
    if (!x) return;
    t[x].sz=1+t[t[x].c[0]].sz+t[t[x].c[1]].sz;
}

void Pushdown(int x) {
    if (!x||!t[x].rev) return;
    t[t[x].c[0]].rev^=1;t[t[x].c[1]].rev^=1;
    int p=t[x].c[0];t[x].c[0]=t[x].c[1];t[x].c[1]=p;
    t[x].rev=0;
}

bool Witch(int x) {return t[t[x].f].c[1]==x;}

void Rotate(int x) {
    int f=t[x].f,gf=t[f].f,lr=Witch(x);
    t[x].f=gf;if (gf) t[gf].c[Witch(f)]=x;
    t[f].c[lr]=t[x].c[lr^1];t[t[f].c[lr]].f=f;
    t[x].c[lr^1]=f;t[f].f=x;
    Update(f);Update(x);
}

void Splay(int x,int goal) {
    Pushdown(x);
    for (;t[x].f!=goal;Rotate(x))
        if (t[t[x].f].f!=goal)Rotate(Witch(t[x].f)==Witch(x)?t[x].f:x);
    if (!goal) rt=x;
    Update(x);
}

void Build(int &x,int l,int r) {
    if (l>r) return;
    if (!x) x=++cnt;
    int mid=l+r>>1;
    t[x].val=mid;t[x].sz=1;
    Build(t[x].c[0],l,mid-1);Build(t[x].c[1],mid+1,r);
    t[t[x].c[0]].f=t[t[x].c[1]].f=x;
    Update(x);
}

int Get(int x,int y,int goal) {
    Pushdown(x);
    if (t[t[x].c[0]].sz+1>y) return Get(t[x].c[0],y,goal);
    else {
        if (t[t[x].c[0]].sz+1==y) return x;
        return Get(t[x].c[1],y-t[t[x].c[0]].sz-1,goal);
    }
}

void Print(int x) {
    Pushdown(x);
    if (t[x].c[0]) Print(t[x].c[0]);
    if (t[x].val>=1&&t[x].val<=n)printf("%d ",t[x].val);
    if (t[x].c[1]) Print(t[x].c[1]);
}

void Change(int l,int r) {
    int x1,x2;
    x1=Get(rt,l,0);x2=Get(rt,r+2,rt);
    Splay(x1,0);Splay(x2,x1);Update(rt);
    t[t[x2].c[0]].rev^=1;
    Update(t[x2].c[0]);Update(x2);Update(rt);

}

int main() {
    scanf("%d%d",&n,&m);
    Build(rt,0,n+1);
    for (int i=1;i<=m;i++) {
        int l,r;
        scanf("%d%d",&l,&r);
        Change(l,r);
    }
    Print(rt);
}

原文地址:https://www.cnblogs.com/mastervan/p/10293956.html

时间: 2024-10-07 05:05:24

[Splay]luogu P3391 文艺平衡树的相关文章

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,

【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,

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次变换后的结果

【模板】文艺平衡树(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