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

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

個數 mes 區間 翻轉 kth rand() 中序 clu span

文藝平衡樹的Fhq-Treap(無旋treap)做法

與普通平衡樹不同的是,這裏平衡樹需要維護的是一個序列,怎麽辦呢?

題解

平衡樹的性質:無論哪個操作都不會改變樹的中序遍歷

所以,對於一個序列平衡樹來說,可以通過中序遍歷來維護整個數組,那麽對應的下標為\(k\)的值就相當於平衡樹中的\(Kth\)

那麽翻轉怎麽辦呢?

其實翻轉就相當於恰好包含整個區間的子樹將其所有兒子節點的左右子樹翻轉!
於是就可以通過無旋\(treap\)來解決這個問題。

每次翻轉\([l,r]\)我們就把數組為\(a_{1}-a_{l-1}\)\(a_{l}-a_{r}\)\(a_{r+1}-a_{n}\)分別\(Split\)

成三棵樹,將\(a_{l}-a_{r}\)打上\(rev\)標記再\(Merge\)回去。

記住在每一次\(Split\)\(Merge\)操作前將標記下傳即可。

代碼

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch==‘-‘?f=-1:0,ch=getchar());
    for(;isdigit(ch);x=x*10+ch-‘0‘,ch=getchar());
    return x*f;
}
bool rev[100005];
int val[100005],size[100005],ls[100005],rs[100005],pri[100005],R,n,m,cnt;
void Pushdown(int p){if(rev[p])swap(ls[p],rs[p]),rev[ls[p]]^=1,rev[rs[p]]^=1,rev[p]=0;}
void Pushup(int p){size[p]=size[ls[p]]+size[rs[p]]+1;}
void Split(int p,int k,int&rt1,int&rt2){
    if(!p){rt1=rt2=0;return;}
    Pushdown(p);
    if(k<=size[ls[p]]){Split(ls[p],k,rt1,rt2);ls[p]=rt2;rt2=p;}
    else{Split(rs[p],k-size[ls[p]]-1,rt1,rt2);rs[p]=rt1;rt1=p;}
    Pushup(p);
}
int Merge(int rt1,int rt2){
    if(!rt1)return rt2;if(!rt2)return rt1;
    Pushdown(rt1),Pushdown(rt2);
    if(pri[rt1]<pri[rt2]){rs[rt1]=Merge(rs[rt1],rt2);Pushup(rt1);return rt1;}
    else{ls[rt2]=Merge(rt1,ls[rt2]);Pushup(rt2);return rt2;}
}
void Insert(int v){val[++cnt]=v;pri[cnt]=rand();size[cnt]=1;R=Merge(R,cnt);}
void Print(int p){if(!p)return;Pushdown(p);Print(ls[p]);printf("%d ",val[p]);Print(rs[p]);}
int main(){
    n=read(),m=read();
    srand(time(0));
    for(int i=1;i<=n;++i)Insert(i);
    for(int i=1;i<=m;++i){
        int l=read(),r=read(),rt1,rt2,rt3,rt4;
        Split(R,l-1,rt1,rt2);
        Split(rt2,r-l+1,rt3,rt4);
        rev[rt3]^=1;
        R=Merge(rt1,Merge(rt3,rt4));
    }
    Print(R);
}

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