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

Posted niiick

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷 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伸展,建树,操作等操作看这里
Splay—平衡树学习笔记

毕竟splay是个挺暴力的玩意儿
所以肯定不能直接暴力反转区间
所以我们借用线段树思想
用lzy[u]=1表示以u为根的子树需要反转

然而问题是怎么找到需要反转的区间呢
假设我们要得到 ll-rr 区间
我们先找到当前序列第ll-1个元素(设为x)和第rr+1个元素(设为y)
将x旋转到rt,再将y旋转到ch[rt][1]
这时ch[y][0],也就是根的右儿子的左子树
就是我们要的区间
仔细思考,是不是很神奇

注意因为要旋转ll-1与rr+1
所以序列需要加两个哨兵结点1与n+2
而原序列就变成了2-n+1
记得每次输入的操作区间要先加1

到这里我们只要给ch[y][0]打上标记就好了
然后在需要的时候下放标记

void push(int p)
{
    if(!lzy[p]) return;
    swap(ch[p][0],ch[p][1]);//交换左右子树
    lzy[ch[p][1]]^=1; lzy[ch[p][0]]^=1;//下放标记
    lzy[p]=0;
}

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;

int read()
{
    int x=0,f=1;
    char ss=getchar();
    while(ss<‘0‘||ss>‘9‘){if(ss==‘-‘)f=-1;ss=getchar();}
    while(ss>=‘0‘&&ss<=‘9‘){x=x*10+ss-‘0‘;ss=getchar();}
    return f*x;
}

int n,m;
int rt;
int size[100010],fa[100010],ch[100010][2];
int lzy[100010];

void update(int p)
{
    size[p]=size[ch[p][0]]+size[ch[p][1]]+1;
}

void push(int p)
{
    if(!lzy[p]) return;
    swap(ch[p][0],ch[p][1]);//交换左右子树
    lzy[ch[p][1]]^=1; lzy[ch[p][0]]^=1;//下放标记
    lzy[p]=0;
}

void build(int p,int ll,int rr)
{
    if(ll>rr) return;
    int mid=ll+rr>>1;
    fa[mid]=p; size[mid]=1;
    ch[p][mid>p]=mid;
    if(ll==rr) return;
    build(mid,ll,mid-1);build(mid,mid+1,rr);
    update(mid); 
}

int find(int p,int k) 
{ 
    push(p);  
    int ss=size[ch[p][0]]; 
    if(k==ss+1) return p;    
    if(k<=ss) return find(ch[p][0],k);
    else return find(ch[p][1],k-ss-1);  
}  

void rotate(int& p,int x) { 
    int y=fa[x],z=fa[y]; 
    int t=(ch[y][0]==x);
    if(y==p) p=x; 
    else if(ch[z][0]==y) ch[z][0]=x; 
    else ch[z][1]=x;  
    fa[y]=x; fa[ch[x][t]]=y; fa[x]=z;  
    ch[y][t^1]=ch[x][t]; ch[x][t]=y;   
    update(y);update(x);  
} 

void splay(int& p,int x) 
{   
    while(x!=p) 
    {
        int y=fa[x],z=fa[y];
        if(y!=p) 
        {
            if((ch[y][0]==x)^(ch[z][0]==y)) rotate(p,x);
            else rotate(p,y);
        }
        rotate(p,x);
    }
}

void rev(int ll,int rr)
{
    int x=find(rt,ll-1),y=find(rt,rr+1);//找到第ll-1和rr+1个元素编号
    splay(rt,x);
    splay(ch[x][1],y);  //伸展
    lzy[ch[y][0]]^=1;//标记
}

int main() {
    n=read();m=read();
    rt=n+3>>1;//因为加了两个哨兵结点,所以rt是(n+3)/2
    build(rt,1,n+2);

    while(m--)
    {
        int ll=read(),rr=read();
        rev(ll+1,rr+1);//操作区间后移
    }
    for(int i=2;i<=n+1;++i) 
    printf("%d ",find(rt,i)-1);//输出相当于从1开始每次找到第k个元素
    return 0;//当然也可以按照中序遍历输出
}

以上是关于洛谷 P3391 模板文艺平衡树(Splay)的主要内容,如果未能解决你的问题,请参考以下文章

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

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

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

P3391 模板文艺平衡树(Splay)

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

P3391 模板文艺平衡树(Splay)