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

2022-03-04 21:59:53 字數 4841 閱讀 9246

這是一道經典的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]資料保證 1≤l≤r≤n

輸出格式:

輸出一行n個數字,表示原始序列經過m次變換後的結果

輸入樣例#1:

5 3

1 31 3

1 4

輸出樣例#1:

4 3 2 1 5

n,m≤100000 n, m

1 簡介:

伸展樹,或者叫自適應查詢樹,是一種用於儲存有序集合的簡單高效的資料結構。伸展樹實質上是乙個二叉查詢樹。允許查詢,插入,刪除,刪除最小,刪除最大,分割,合併等許多操作,這些操作的時間複雜度為o(logn)。由於伸展樹可以適應需求序列,因此他們的效能在實際應用中更優秀。

伸展樹支援所有的二叉樹操作。伸展樹不保證最壞情況下的時間複雜度為o(logn)。伸展樹的時間複雜度邊界是均攤的。儘管乙個單獨的操作可能很耗時,但對於乙個任意的操作序列,時間複雜度可以保證為o(logn)。

2 自調整和均攤分析:

平衡查詢樹的一些限制:

1、平衡查詢樹每個節點都需要儲存額外的資訊。

2、難於實現,因此插入和刪除操作複雜度高,且是潛在的錯誤點。

3、對於簡單的輸入,效能並沒有什麼提高。

平衡查詢樹可以考慮提高效能的地方:

1、平衡查詢樹在最差、平均和最壞情況下的時間複雜度在本質上是相同的。

2、對乙個節點的訪問,如果第二次訪問的時間小於第一次訪問,將是非常好的事情。

3、90-10法則。在實際情況中,90%的訪問發生在10%的資料上。

4、處理好那90%的情況就很好了。

3 均攤時間邊界:

在一顆二叉樹中訪問乙個節點的時間複雜度是這個節點的深度。因此,我們可以重構樹的結構,使得被經常訪問的節點朝樹根的方向移動。儘管這會引入額外的操作,但是經常被訪問的節點被移動到了靠近根的位置,因此,對於這部分節點,我們可以很快的訪問。根據上面的90-10法則,這樣做可以提高效能。

為了達到上面的目的,我們需要使用一種策略──旋轉到根(rotate-to-root)。

上面所說的,我來舉個例子解釋一下:假設有這樣一道題,有100000次操作,每次輸入a,b,若a為0表示將b放入數列中,若a為1表示輸出第b大的數。這道題看似簡單,不就直接二叉搜尋樹嘛!但是如果數b是單調遞增出現的,則樹會成鏈,那麼還是o(n^2)的複雜度。此時我們邊用到了splay,由於splay是不斷翻轉的,所以就算某一時刻他成了一條鏈,也會馬上旋轉而變成另外的形態(深度減低),通過這樣不斷地變換可以防止長期停留在鏈的狀態,以保證每次操作平均複雜度o(log n)。

關於splay,我覺得自己已經完全掌握了,讓我口頭說還可以,但是要寫篇詳解實在是時間又少而且沒精力(而且大神們的部落格已經寫的非常到位了,自己寫的肯定不及他們),所以這裡我提供本人自學splay時所看的一些比較有用的部落格:1、基礎(非指標)

2、基礎(指標)

3、應用

認真看上述部落格並思考,便會發現splay其實很簡單。

splay tree可以方便的解決一些區間問題,根據不同形狀二叉樹先序遍歷結果不變的特性,可以將區間按順序建二叉查詢樹。

每次自下而上的一套splay都可以將x移動到根節點的位置,利用這個特性,可以方便的利用lazy的思想進行區間操作。

對於每個節點記錄size,代表子樹中節點的數目,這樣就可以很方便地查詢區間中的第k小或第k大元素。

對於一段要處理的區間[x, y],首先splay x-1到root,再splay y+1到root的右孩子,這時root的右孩子的左孩子對應子樹就是整個區間。

這樣,大部分區間問題都可以很方便的解決,操作同樣也適用於乙個或多個條目的新增或刪除,和區間的移動。

操作:1. 插入x數

2. 刪除x數(若有多個相同的數,因只刪除乙個)

3. 查詢x數的排名(若有多個相同的數,因輸出最小的排名)

4. 查詢排名為x的數

5. 求x的前驅(前驅定義為小於x,且最大的數)

6. 求x的後繼(後繼定義為大於x,且最小的數)

只要我們弄懂splay,其實本題很簡單:首先按照中序遍歷建樹,然後對於每次修改區間l,r,首先得提出這段區間,方法是將l的前趨l-1旋轉到根節點,將r的後趨r+1旋轉到根節點的右兒子,我們可以自己畫圖試試,容易發現經過這個操作後,根節點的右兒子的左子樹(具體應該說是這個左子樹的中序遍歷)就是區間l-r。關鍵的翻轉時,因為樹是中序遍歷(左根右),所以我們只要將l-r(前面所說的根節點的右兒子的左子樹)這個區間子樹左右兒子的節點交換位置(這樣再中序遍歷相當於右根左,即做到了翻轉操作)。關鍵是翻轉的優化,我們用到懶惰標記lazy[x](表示x是否翻轉),每次翻轉時只要某個節點有標記且在翻轉的區間內,則將標記下放給它的兩個兒子節點且將自身標記清0,這樣便避免了多餘的重複翻轉。(不懂畫圖看部落格)

1、裸**:

//

luogu-judger-enable-o2

#include#define ll long long

#define il inline

#define debug printf("%d %s\n",__line__,__function__)

using

namespace

std;

const

int n=100005

;il

intgi()

int n,m,tot,root,siz[n],fa[n],flag[n],key[n],ch[n][2

],cnt[n],ans[n];

il void update(int

rt)il

void pushdown(int

now)

}il

int getson(int x)

il void rotate(int

x)il

void splay(int x,int

i)

else}}

}il

int find(int

x) }

}il

int build(int l,int r,int

rt)il

void print(int

now)

intmain()

print(root);

for(int i=1;i<=n;i++)printf("

%d ",ans[i+1

]);

return0;

}

2、方便理解,帶注釋**:

/*

splay只記模板是很困難的,而且真正運用時易生疏出錯,所以必須理解,在看**前先弄懂

splay的原理,這篇**是帶注釋的splay模板,題目來自洛谷p3391 ———————————by 520

*/#include

#define il inline

#define debug printf("%d %s\n",__line__,__function__)

using

namespace

std;

const

int n=100005

;il

intgi()

int n,m,tot,root,siz[n],fa[n],lazy[n],key[n],tree[n][2

],ans[n];

/*root為根節點,siz儲存子樹節點數,fa儲存父節點,lazy是懶惰標記用來標記區間翻轉操作,key陣列儲存原數列,tree為

splay樹,ans儲存答案

*/il

void pushup(int rt) //

作用類似與線段樹

il void pushdown(int

now)

}il

int getson(int x) //

getson判斷x是其父親的右兒子還是左兒子

il void rotate(int x) //

旋轉操作,直接寫在乙個函式裡,可以稱為上旋

il void splay(int x,int

i) /*

若x和y為相同偏向,則進行zig-zig或zag-zag操作

*/else /*

否則進行zig-zag或zag-zig操作

*//*

注意rotate函式中已經包含了這四種操作情況了*/}

}}il

int find(int x) //

查詢x的位置

}}il

int build(int l,int r,int rt) //

建樹過程和線段樹類似

il void print(int now) //

輸出時中序遍歷,按左根右輸出

intmain()

print(root);

for(int i=1;i<=n;i++)printf("

%d ",ans[i+1]); //

輸出時將前驅還原為原數

return0;

}

洛谷 P3391 模板 文藝平衡樹

真正的模板題,先用splay把這個題打熟了再來做這個題qwq splay的基本操作我就不講了,直接說一說這個題的做法 首先我們把序列放到一棵樹上,使得這棵樹的中序遍歷為原序列,這個建樹操作和線段樹類似,遞迴建立左右兒子,然後進行兩個操作 f in dfind find 獲取序列第x xx位置上的值 ...

洛谷 P3391 模板 文藝平衡樹

這是一道經典的splay模板題 文藝平衡樹。您需要寫一種資料結構,來維護乙個有序數列,其中需要提供以下操作 翻轉乙個區間,例如原有序序列是5 4 3 2 1,翻轉區間是 2,4 的話,結果是5 2 3 4 1 輸入格式 第一行為n,m n,m 100000 n表示初始序列有n個數,這個序列依次是 1...

洛谷P3391 模板 文藝平衡樹

您需要寫一種資料結構 可參考題目標題 來維護乙個有序數列。其中需要提供以下操作 翻轉乙個區間,例如原有序序列是 5 4 3 2 1 翻轉區間是 2,4 的話,結果是 5 2 3 4 1 splay 模板。總算是會摳 splay 了。treap 採用鍵值隨機化來維護 bst 平衡,而 splay 則採...