1. 程式人生 > >洛谷 P3391 【模板】文藝平衡樹(Splay)

洛谷 P3391 【模板】文藝平衡樹(Splay)

是我 art AI n+2 ota algo 結果 交換 例如

題目背景

這是一道經典的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)