1. 程式人生 > >[Splay]luogu P3391 文藝平衡樹

[Splay]luogu P3391 文藝平衡樹

mes hide 我們 ++ else closed src n+1 clas

題目描述

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 <iostream>
#include <cstdio>
using
namespace std; const int N=1e5+10; struct Node { int sz,val,f,c[2]; bool rev; }t[N]; int cnt,rt; int n,m; void Update(int x) { if (!x) return; t[x].sz=1+t[t[x].c[0]].sz+t[t[x].c[1]].sz; } void Pushdown(int x) { if (!x||!t[x].rev) return; t[t[x].c[0]].rev^=1;t[t[x].c[1
]].rev^=1; int p=t[x].c[0];t[x].c[0]=t[x].c[1];t[x].c[1]=p; t[x].rev=0; } bool Witch(int x) {return t[t[x].f].c[1]==x;} void Rotate(int x) { int f=t[x].f,gf=t[f].f,lr=Witch(x); t[x].f=gf;if (gf) t[gf].c[Witch(f)]=x; t[f].c[lr]=t[x].c[lr^1];t[t[f].c[lr]].f=f; t[x].c[lr
^1]=f;t[f].f=x; Update(f);Update(x); } void Splay(int x,int goal) { Pushdown(x); for (;t[x].f!=goal;Rotate(x)) if (t[t[x].f].f!=goal)Rotate(Witch(t[x].f)==Witch(x)?t[x].f:x); if (!goal) rt=x; Update(x); } void Build(int &x,int l,int r) { if (l>r) return; if (!x) x=++cnt; int mid=l+r>>1; t[x].val=mid;t[x].sz=1; Build(t[x].c[0],l,mid-1);Build(t[x].c[1],mid+1,r); t[t[x].c[0]].f=t[t[x].c[1]].f=x; Update(x); } int Get(int x,int y,int goal) { Pushdown(x); if (t[t[x].c[0]].sz+1>y) return Get(t[x].c[0],y,goal); else { if (t[t[x].c[0]].sz+1==y) return x; return Get(t[x].c[1],y-t[t[x].c[0]].sz-1,goal); } } void Print(int x) { Pushdown(x); if (t[x].c[0]) Print(t[x].c[0]); if (t[x].val>=1&&t[x].val<=n)printf("%d ",t[x].val); if (t[x].c[1]) Print(t[x].c[1]); } void Change(int l,int r) { int x1,x2; x1=Get(rt,l,0);x2=Get(rt,r+2,rt); Splay(x1,0);Splay(x2,x1);Update(rt); t[t[x2].c[0]].rev^=1; Update(t[x2].c[0]);Update(x2);Update(rt); } int main() { scanf("%d%d",&n,&m); Build(rt,0,n+1); for (int i=1;i<=m;i++) { int l,r; scanf("%d%d",&l,&r); Change(l,r); } Print(rt); }
View Code

[Splay]luogu P3391 文藝平衡樹