洛谷 P3391 【模板】文藝平衡樹(Splay)
阿新 • • 發佈:2018-04-05
是我 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)