【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]].tag^=1;
    tree[tree[x].e[1]].tag^=1;
    tree[x].tag=0;
}

  这就是标记下传

  

#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using std::swap;

inline long long read(){
    long long num=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch==‘-‘)    f=-1;
        ch=getchar();
    }
    while(isdigit(ch)){
        num=num*10+ch-‘0‘;
        ch=getchar();
    }
    return num*f;
}

int root;int n;int m;

struct Splay{
    struct node{
        int e[2],size,fa;
        bool tag;
    }tree[200000];
    inline int iden(int x){    return x==tree[tree[x].fa].e[1];    }
    inline void update(int x){    tree[x].size=tree[tree[x].e[0]].size+tree[tree[x].e[1]].size+1;    }
    inline void connect(int x,int fa,int how){    tree[x].fa=fa;    tree[fa].e[how]=x;    }
    void rotate(int x){
        int y=tree[x].fa;    if(y==root)    root=x;
        int r=tree[y].fa;
        //pushdown(r);    pushdown(y);    pushdown(x);
        int sony=iden(x);    int sonr=iden(y);
        int b=tree[x].e[sony^1];
        connect(b,y,sony);
        connect(y,x,sony^1);
        connect(x,r,sonr);
        update(y);    update(x);
    }
    inline void pushdown(int x){
        if(!tree[x].tag)    return;
        swap(tree[x].e[0],tree[x].e[1]);
        tree[tree[x].e[0]].tag^=1;
        tree[tree[x].e[1]].tag^=1;
        tree[x].tag=0;
    }
    void splay(int pos,int to){
        while(tree[pos].fa!=to){
            if(tree[tree[pos].fa].fa==to)    rotate(pos);
            else
                if(iden(pos)==iden(tree[pos].fa)){    rotate(tree[pos].fa);    rotate(pos);    }
                else    {    rotate(pos);    rotate(pos);    }
        }
        update(pos);
    }
    int build(int l,int r){
        if(l>r)    return 0;
        int mid=(l+r)>>1;
        int lson=build(l,mid-1);
        connect(lson,mid,0);
        int rson=build(mid+1,r);
        connect(rson,mid,1);
        tree[mid].tag=0;
        update(mid);
        return mid;
    }
    int find(int val){
        int now=root;val--;
        pushdown(now);
        while(val!=tree[tree[now].e[0]].size){
            if(tree[tree[now].e[0]].size<val){
                val-=tree[tree[now].e[0]].size+1;
                now=tree[now].e[1];
            }
            else    now=tree[now].e[0];
            pushdown(now);
        }
        return now;
    }
    void print(int now){
        if(!now)    return;
        pushdown(now);
        print(tree[now].e[0]);
        if(now!=1&&now!=n+2)    printf("%d ",now-1);
        print(tree[now].e[1]);
    }
}s;

int main(){
    n=read();    m=read();    root=s.build(1,n+2);
    while(m--){
        int l=read(),r=read();
        int x=s.find(l);    s.splay(x,0);
        int y=s.find(r+2);    s.splay(y,root);
        s.tree[s.tree[y].e[0]].tag^=1;
    }
    s.print(root);
    return 0;
}
时间: 2024-10-07 05:05:23

【Luogu】P3391文艺平衡树(Splay)的相关文章

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,

[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)

题目背景 这是一道经典的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次变换后的结果

Tyvj P1729 文艺平衡树 Splay

题目: http://tyvj.cn/p/1729 P1729 文艺平衡树 时间: 1000ms / 空间: 131072KiB / Java类名: Main 背景 此为平衡树系列第二道:文艺平衡树 描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 输入格式 第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2……n-1,n)  m表示翻转操作次数

BZOJ3223: Tyvj 1729 文艺平衡树 [splay]

3223: Tyvj 1729 文艺平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 3595  Solved: 2029[Submit][Status][Discuss] Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 Input 第一行为n,m n表示初始序列有n个数,这个序列依次

[BZOJ3223] [Tyvj1729] 文艺平衡树 (splay)

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

JZYZOJ1998 [bzoj3223] 文艺平衡树 splay 平衡树

http://172.20.6.3/Problem_Show.asp?id=1998 平衡树区间翻转的板子,重新写一遍,给自己码一个板子. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #include<cstdlib> 7 using namespace std; 8 cons

[Bzoj3223][Tyvj1729] 文艺平衡树(splay/无旋Treap)

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3223 平衡树处理区间问题的入门题目,普通平衡树那道题在维护平衡树上是以每个数的值作为维护的标准,而处理区间问题时,维护平衡树的应该是每个位置的下标,所以平衡树中序遍历时应该是当前区间的样子.例如: {1 2 3 4 5}翻转区间1 3,则中序遍历应该输出{3,2,1,4,5}. 提供splay和无旋Treap的做法. splay做法: 1 #include<bits/stdc++.h>

bzoj 3223 文艺平衡树 Splay 打标志

是NOI2003Editor的一个子任务 1 #include <cstdio> 2 #include <vector> 3 #define maxn 100010 4 using namespace std; 5 6 struct Splay { 7 int pre[maxn], son[maxn][2], siz[maxn], rev[maxn], root; 8 9 void update( int nd ) { 10 siz[nd] = siz[son[nd][0]]+si