1. 程式人生 > >平衡樹【Splay】區間翻轉【模板】

平衡樹【Splay】區間翻轉【模板】

luoguP3391一道模板題 學習了splaysplay區間翻轉的姿勢

具體就是先建兩個虛擬節點1,n+21,n+2,然後按節點下標+1+1將區間樹建出來,這時候排名為kk的就是第kk個數。 翻轉的時候先把(l+1)1(l+1)-1提到根,再把(r+1)+1(r+1)+1提到根的右兒子,然後(r+1)+1(r+1)+1的左兒子就是一整個區間了,然後把它打上revrev標記,findfind查詢排名的時候記得下放一下標記,pushdownpushdown的時候只要swapswap一下左右兒子就好啦。

昨天寫的splaysplay是直接旋到根的,今天寫了一下旋轉到固定點的qwqqwq

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 100005
using namespace std;
int n,m,ch[N][2],siz[N],fa[N],root,rev[N];

inline int rd(){
	int x=0,f=1;char c=' ';
	while(c<
'0' || c>'9') f=c=='-'?-1:1,c=getchar(); while(c<='9' && c>='0') x=x*10+c-'0',c=getchar(); return x*f; } inline void pushup(int x){ siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1; } inline void pushdown(int x){ if(rev[x]){ swap(ch[x][0],ch[x][1]); rev[ch[x][0]]^=1,rev[ch[x][1]]^=1; rev[x]=0;
} } inline int get(int x){return ch[fa[x]][1]==x;} inline void rotate(int x,int &k){ int old=fa[x],oldf=fa[old],wh=get(x); if(old==k) k=x; else ch[oldf][ch[oldf][1]==old]=x; fa[ch[x][wh^1]]=old; ch[old][wh]=ch[x][wh^1]; fa[old]=x; ch[x][wh^1]=old; fa[x]=oldf; pushup(old); pushup(x); } inline void splay(int x,int &k){//將x旋轉到k while(x!=k){ int y=fa[x],z=fa[y]; if(y!=k){ if(get(x)==get(y)) rotate(y,k); else rotate(x,k); } rotate(x,k); } } void build(int x,int l,int r){//建樹 if(l>r) return; int mid=(l+r)>>1; if(mid<x) ch[x][0]=mid; else ch[x][1]=mid; fa[mid]=x; siz[mid]=1; if(l==r) return; build(mid,l,mid-1); build(mid,mid+1,r); pushup(mid); } int find(int x,int k){//查詢排名為k的點 pushdown(x); int s=siz[ch[x][0]]; if(k==s+1) return x; if(k<=s) return find(ch[x][0],k); else return find(ch[x][1],k-s-1); } inline void rever(int l,int r){//翻轉 int x=find(root,l),y=find(root,r+2);//l-1,r+1 splay(x,root); splay(y,ch[x][1]); rev[ch[y][0]]^=1; } int main(){ n=rd(); m=rd(); root=(n+3)>>1; build(root,1,n+2); while(m--){ int l=rd(),r=rd(); rever(l,r); } for(int i=2;i<=n+1;i++) printf("%d ",find(root,i)-1); return 0; }