題解 bzoj4825 HNOI2017單旋

2021-08-06 01:22:52 字數 3882 閱讀 7065

h 國是乙個熱愛寫**的國家,那裡的人們很小去學校學習寫各種各樣的資料結構。伸展樹(splay)是一種資料結構,因為**好寫,功能多,效率高,掌握這種資料結構成為了 h 國的必修技能。有一天,**的「卡」帶著他的**的「常數」來企圖毀滅 h 國。「卡」給 h 國的人**說,splay 如果寫成單旋的,將會更快。「卡」稱「單旋 splay」為「spaly」。雖說他說的很沒道理,但還是有 h 國的人相信了,小 h 就是其中之一,spaly 馬上成為他的信仰。 而 h 國的國王,自然不允許這樣的風氣蔓延,國王構造了一組資料,資料由 m 個操作構成,他知道這樣的資料肯定打垮 spaly,但是國王還有很多很多其他的事情要做,所以統計每個操作所需要的實際代價的任務就交給你啦。

資料中的操作分為五種:

1. 插入操作:向當前非空 spaly 中插入乙個關鍵碼為 key 的新孤立節點。插入方法為,先讓 key 和根比較,如果 key 比根小,則往左子樹走,否則往右子樹走,如此反覆,直到某個時刻,key 比當前子樹根 x 小,而 x 的左子樹為空,那就讓 key 成為 x 的左孩子; 或者 key 比當前子樹根 x 大,而 x 的右子樹為空,那就讓 key 成為 x 的右孩子。該操作的代價為:插入後,key 的深度。特別地,若樹為空,則直接讓新節點成為乙個單個節點的樹。(各節點關鍵碼互不相等。對於「深度」的解釋見末尾對 spaly 的描述)。

2. 單旋最小值:將 spaly 中關鍵碼最小的元素 xmin 單旋到根。操作代價為:單旋前 xmin 的深度。(對於單旋操作的解釋見末尾對 spaly 的描述)。

3. 單旋最大值:將 spaly 中關鍵碼最大的元素 xmax 單旋到根。操作代價為:單旋前 xmax 的深度。

4. 單旋刪除最小值:先執行 2 號操作,然後把根刪除。由於 2 號操作之後,根沒有左子樹,所以直接切斷根和右子樹的聯絡即可(具體見樣例解釋)。 操作代價同 2 號操 作。

5. 單旋刪除最大值:先執行 3 號操作,然後把根刪除。 操作代價同 3 號操作。對於不是 h 國的人,你可能需要了解一些 spaly 的知識,才能完成國王的任務:

a. spaly 是一棵二叉樹,滿足對於任意乙個節點 x,它如果有左孩子 lx,那麼 lx 的關鍵碼小於 x 的關鍵碼。如果有右孩子 rx,那麼 rx 的關鍵碼大於 x 的關鍵碼。

b. 乙個節點在 spaly 的深度定義為:從根節點到該節點的路徑上一共有多少個節點(包括自己)。

c. 單旋操作是對於一棵樹上的節點 x 來說的。一開始,設 f 為 x 在樹上的父親。如果 x 為 f 的左孩子,那麼執行 zig(x) 操作(如上圖中,左邊的樹經過 zig(x) 變為了右邊的樹),否則執行 zag(x) 操作(在上圖中,將

右邊的樹經過 zag(f) 就變成了左邊的樹)。每當執 行一次 zig(x) 或者 zag(x),x 的深度減小 1,如此反覆,直到 x 為根。總之,單旋 x 就是通過反覆執行 zig 和 zag 將 x 變為根。

第一行單獨乙個正整數 m。

接下來 m 行,每行描述乙個操作:首先是乙個操作編號 c∈[1,5],即問題描述中給出的五種操作中的編號,若 c = 1,則再輸入乙個非負整數 key,表示新插入節點的關鍵碼。1≤m≤10^5,1≤key≤10^9

所有出現的關鍵碼互不相同。任何乙個非插入操作,一定保證樹非空。在未執行任何操作之前,樹為空

輸出共 m 行,每行乙個整數,第 i 行對應第 i 個輸入的操作的代價。

5 1 2

1 1

1 3 4 5

1 2

2 2

2單旋的時候對樹的形態破壞不大,只會影響少數節點,所以用splay維護深度就好了,自己手動分析一下影響,同時記錄下在真正的spaly樹上的父親、左右兒子。細節多如牛毛,真心難調。

**:

#include

#include

using

namespace

std;

template

inline

void read(t &x)

const

int maxn=100010,inf=0x7fffffff;

struct node

}t[maxn];

struct splay_tree

int getson(int x)

void pushdown(int x)

if(t[x].ch[1])

t[x].add=0;

}void rotate(int x)

void splay(int x,int f=0)

void insert(int val,int dp)

int x=root,fa;

while(x)

t[fa].ch[val>t[fa].key]=++num;

t[num].key=val;t[num].dep=dp;

t[num].fa=fa;

splay(num);

}int find(int val)

}int getpre(int val)

return ans;

}int getsuf(int val)

return ans;

}int getmin()

int getmax()

}tree;

int n;

int main()

else

if(r==inf)

else

else}}

else

if(opt==2)

if(t[x].rch[1])

else t[t[x].***].rch[0]=0;

t[x].rch[1]=tree.rroot;

t[tree.rroot].***=x;

t[x].***=t[x].rch[0]=0;

tree.rroot=x;}}

else

if(opt==3)

if(t[x].rch[0])

else t[t[x].***].rch[1]=0;

t[x].rch[0]=tree.rroot;

t[tree.rroot].***=x;

t[x].***=t[x].rch[1]=0;

tree.rroot=x;}}

else

if(opt==4)

if(t[x].rch[1])

else t[t[x].***].rch[0]=0;

t[x].rch[1]=tree.rroot;

t[tree.rroot].***=x;

t[x].***=t[x].rch[0]=0;

tree.rroot=x;

}tree.pushdown(tree.root);

tree.rroot=t[tree.rroot].rch[1];

t[tree.rroot].***=0;

tree.root=t[tree.root].ch[1];

t[tree.root].fa=0;

if(tree.root)

}else

if(opt==5)

if(t[x].rch[0])

else t[t[x].***].rch[1]=0;

t[x].rch[0]=tree.rroot;

t[tree.rroot].***=x;

t[x].***=t[x].rch[1]=0;

tree.rroot=x;

}tree.pushdown(tree.root);

tree.rroot=t[tree.rroot].rch[0];

t[tree.rroot].***=0;

tree.root=t[tree.root].ch[0];

t[tree.root].fa=0;

if(tree.root)

}printf("%d\n",ans);

}return

0;}

題解 BZOJ 4717 裝備

傳送門 由於這道題是許可權題,所以題面我也放在這裡了 我不是許可權狗 題目背景 小q最近喜歡上了一款遊戲,名為 艦隊connection 在遊戲中,小q指揮強大的艦隊南征北戰,從而成為了一名dalao。在遊戲中,不僅船隻能力很重要,搭配合適的裝備更是如虎添翼。小q潛心研究配裝三十年,終於 把裝備湊齊...

題解 BZOJ 3910 火車

3910.火車 給你一棵樹,給你乙個訪問節點的序列,按照先後順序去訪問序列中的從未經過過的節點,問經過了多少條邊。並查集 lca 用並查集維護每個點是否走過,如果走過了就將該點和他的第乙個沒被走過的父親合併。lca 用來計算距離,在路徑上暴跳的時候維護並查集,因為每個點最多被經過一次,複雜度 o n...

題解 BZOJ2952 長跑

看到題目即可想到使用lct 然而我們發現,只維護點之間的關係是不行的,因為圖上可能會構成環 因此我們將邊雙縮點,發現縮完點後原圖變成了森林 因此就可以直接用lct做了 接下來,我們考慮如何在邊雙縮點的同時維護lct 首先,我們對於每乙個點都要再維護該點所在邊雙的權值和 另外,lct的操作中,凡是涉及...