Splay bzoj3223文藝平衡樹

2021-08-27 08:49:35 字數 2980 閱讀 5202

splay,中文名伸展樹,是由tarjan大神發明的… orz

本質上就是bst加上splay操作——把結點x旋轉到指定結點的下面。

每次查詢完都把查到的數旋轉到根,就可以讓所有查詢的時間效率為均攤o(logn) (不知道為啥…大佬說是就是吧orz)

因為splay可以通過伸展操作隨意改變樹的結構,只要把排名l-1的結點伸展到根,把排名r+1的結點伸展到根的右孩子,r+1結點的左子樹就包含了區間[l,r]中所有的點,所以splay做區間操作非常方便。

缺點大概就是大得驚人的常數吧。

splay的定義:

int ch[maxn][2], fa[maxn], val[maxn], siz[maxn], lazy[maxn];

int tot,root;

#define ls ch[now][0]

#define rs ch[now][1]

跟treap的定義基本一樣的,少個優先順序,多個fa陣列記錄該結點的父親。lazy是題目要用的翻轉標記。

巨集是用來偷懶的~

為什麼treap不要fa,splay要?

因為splay操作要用到父親的父親,不記錄fa不會寫。

旋轉rotate:

跟所有平衡樹的旋轉都是一樣的。get函式用來判斷該結點是它父親的哪個兒子。因為要維護區間,有些地方要pushup(把x旋轉到y的上面之後,y的子樹大小要重新計算)

貼張醜圖以供想象

get(int x)//返回x是它父親的哪個兒子

void connect(int son,int f,int dir)

int y,z,yson,zson,xson;

void rotate(int x)//把x旋轉到fa[x]的位置,用get來判斷要左旋還是右旋

伸展splay——把x旋轉到to的下面,成為to的兒子

三種情況:

1.fa[fa[x]] == to

往上旋一次即可

2.get(x) == get(fa[x])

設y是x的父親,z是y的父親,這種情況下,xyz是共線的(自行腦補)

tarjan大佬說這種情況要先把y旋上去,再把x旋上去

3.其他情況

x往上旋兩次

void splay(int x,int to)//把x旋轉到to的下面

pushup(x);//旋轉後會子樹大小會變,更新

if(to == 0)//x移到根了,更新根

root = x;

}

建樹build:

跟線段樹差不多,多傳乙個引數f用來維護fa陣列

void build(int l,int r,int &now,int f)

更新update(本題的更新是翻轉)

首先把 l-1 splay到root,把 r+1 splay到root下面

build前要插入兩個虛結點0和n+1,不然splay操作會越界

newnode(0,root,0);

newnode(n+1,ch

[root]

[1],root);

build(1,n,ch

[ch[root]

[1]][0],ch

[root]

[1]);

但splay函式是把下標為x的結點旋轉到下標的to的結點下面,這裡的結點編號跟要維護的區間的下標是不一致的

區間就是bst的中序序列,寫個find函式把區間下標對應的結點編號求出來

int findk(int k,int now)//找區間中第k個數字的下標(0是第乙個)

上面也提到了,0是區間中的第乙個數字,所以要splay的是l和r+2

void update(int l,int r)

完整**

#include

#include

using namespace std;

const

int maxn = 1e5 + 5;

int ch[maxn][2], fa[maxn], val[maxn], siz[maxn], lazy[maxn];

int tot,root;

#define ls ch[now][0]

#define rs ch[now][1]

int n,m;

void newnode(int v,int &x,int f)

bool

get(int x)//返回x是它父親的哪個兒子

void pushup(int now)

void pushdown(int now)

}void connect(int son,int f,int dir)

int y,z,yson,zson,xson;

void rotate(int x)//把x旋轉到fa[x]的位置,用get來判斷要左旋還是右旋

void splay(int x,int to)//把x旋轉到to的下面

pushup(x);

if(to == 0)//x移到根了,更新根

root = x;

}void build(int l,int r,int &now,int f)

int findk(int k,int now)//找區間中第k個數字的下標(0是第乙個)

void update(int l,int r)

void dfs(int now)

int main()

dfs(root);

return

0;}

BZOJ 3223 文藝平衡樹

您需要寫一種資料結構 可參考題目標題 來維護乙個有序數列,其中需要提供以下操作 翻轉乙個區間,例如原有序序列是5 4 3 2 1,翻轉區間是 2,4 的話,結果是5 2 3 4 1 n,m 100000 splay拿來練翻轉 這題就只用支援翻轉,很裸 翻轉區間 x,y 的操作 提取該區間,打乙個bo...

bzoj3223 文藝平衡樹

bzoj3223 文藝平衡樹 description 您需要寫一種資料結構 可參考題目標題 來維護乙個有序數列,其中需要提供以下操作 翻轉乙個區間,例如原有序序列是5 4 3 2 1,翻轉區間是 2,4 的話,結果是5 2 3 4 1 input 第一行為n,m n表示初始序列有n個數,這個序列依次...

bzoj 3223 文藝平衡樹

題意 對於乙個1 n的序列。進行m次區間反轉操作 求最後反轉過的區間。n,m 100000。題解 splay躶題。寫完維修數列之後感覺這種題都好寫了。反轉啥的打個標記下傳就好,記得輸出時再pushdown標記就好了 這篇題解就是說一下單旋和雙旋的簡單差別 爺爺結點就是目標的情況不討論了 zig za...