非旋轉Treap 用執行時間換除錯時間的有效手段

2022-05-01 20:27:10 字數 3225 閱讀 7948

。啥意思?如果寫過splay和塊鏈,前者的除錯時間和後者的敲版子時間讓我們頭痛欲裂,但是這個平衡樹,好寫好調,省去了時間。但是常數相對較大,這便是我們的代價(好乙個時間換時間)。

treap就是tree+heap,乙個基於堆的二叉搜尋樹,但是它的維護過程是仰仗於旋轉的。而我們今天講的非旋轉treap,顧名思義,不需要旋轉的哦。

那應該如何維護平衡呢?用兩個基本操作即可:撕裂&合併

撕裂??考慮區間最本質的:如果我們可以將要修改的區間成為一棵單獨的樹,我們是不是想幹什麼就幹什麼!整體操作就打標記,特殊操作就特殊處理。我們也想要這樣怎麼辦?引入乙個split操作,就是斷開原來bst上的一條邊,整個bst變成了兩棵樹,返回乙個pair,pair裡存的是兩棵新bst的根。如果想要提取乙個區間,就split兩次,中間的子樹就是我們要的區間子樹。 附上版子(為了方便理解,我每個語句都是一行):

// split函式的pair存的是兩個新bst的兩個根,時間戳小的在first,時間戳大的在second

pair split(int pos,int num) //表示在以pos為根節點的bst中,取出前num個作為新的bst,剩下的作為另一棵bst

int lson=a[pos].ls;

int rson=a[pos].rs;

// 這裡我們分類討論

if(num==a[lson].size) // 如果恰好是左子樹的大小,我們將左子樹斷開。

// 後面的同理

else if(num==a[lson].size+1)

else if(numa[lson].size+1,說明我們分開的兩棵子樹中有一棵包含了pos的左子樹和根節點。

pair t=split(rson,num-a[lson].size-1); // 所以這裡我們直接將右子樹分成num-a[lson].size-1的兩個子樹。

a[pos].rs=t.first; // 然後將num-a[lson].size-1大小的子樹和前面的樹加在一起,這樣就得到了大小為num的子樹。

pushup(pos);

return make_pair(pos,t.second);

}}

合併??如果我們將一棵單獨的表示區間的bst處理完了,我們需要將兩棵子樹合併到一起啊!所以就有了這個merge操作。因為我們本質上維護平衡的辦法就是利用隨機數的權值來維護乙個依據權值的堆,撕裂操作顯然不會打破這個局勢,但是如果我們瞎合併,就無法保證平衡,時間複雜度也就沒有辦法保證。所以我們想乙個能維護兩個子樹的方法,即能保證堆的性質,也保留bst應該有的中序遍歷時間戳遞增。我們請出可並堆!顯然,如果我們採用可並堆的合併方式,是可以達到我們預期的效果的。

可並堆是如何合併的呢?其實也非常簡單。這樣:因為我們是想維護基於隨機權值val的堆!所以對於兩棵bst,分別以x和y為根。其中,x的整體時間戳小於y。那麼,如果val[x]大於val[y],那麼x一定是他們兩棵bst合併之後的根。因為val[x]是x所在子樹裡最大的,y是y所在子樹裡最大的,x還比y大,所以x是根。又因為,我們要維護tree,所以我們遞迴地將x的右兒子和y合併即可。這樣合併時候的新bst就滿足我們的要求。 附上板子!

// merge 操作返回的是兩棵bst合併後的新bst的根。

int merge(int x,int y)

// 如果x的權值大於y的權值,顯然x是根。我們只需要讓x的右兒子和y合併即可。

if(a[x].val_heap>a[y].val_heap)

// 反之,如果y的權值大於x,顯然y是根。所以我們讓x和y的左兒子合併即可。

else

}

說完了非旋轉treap的基本操作,相信各位對於這個神奇的平衡樹有了一定的認識。它的其他操作有了split和merge後都變得非常簡單,自己手玩即可。

例題時間

bzoj 3223 文藝平衡樹 (所有平衡樹的入門題)

這道題非常經典,我們記錄一下標記,每次merge和split之前pushdown一下即可。 趁著這個題看一下其他的操作哦。

#include #include #include #include #define n 100100

#define mp make_pair

using namespace std;

typedef pairpar;

int ls[n],rs[n],size[n],key[n],val[n];

int n,m,root,tot;

bool lazy[n];

inline void update(int x)

inline void pushdown(int x)

int lson,rson;

par split(int x,int k)

else if(k==size[ls[x]]+1)

else if(k

bzoj 1861 書架

多了兩個get_rank和get_id的操作。

#include #include #include #include #define n 80010

#define mp make_pair

using namespace std;

typedef pairpar;

int n,m,root;

int ls[n],rs[n],size[n],key[n],fa[n];

inline void pushup(int x)

int merge(int x,int y)

ls[y]=merge(x,ls[y]);fa[ls[y]]=y;

pushup(y); return y;

}par split(int x,int k)

if(k==size[lson]+1)

if(k> n >> m ;

for(int x,i=1;i<=n;i++)

char flag[10];

for(int x,y,i=1;i<=m;i++)

else if(flag[0]=='b')

else if(flag[0]=='i')

else

}else if(flag[0]=='a')

else

}return 0;

}

執行時間超時用這個。

方法一,修改php.ini檔案 max execution time 30 maximum execution time of each script,in seconds 把它設定成需要的值就可以了。如果設定成0的話,就是永不過期。方法二,修改php執行檔案 加上 set time limit 0...

計算程式執行時間(acm用)

一些傳統的編譯器,在編譯時就會給出基本的程式跑的時間,有的ide甚至每次跑都會給出時間 如vc6.0 非常方便。但現在一些環境 如vs 就沒有這個功能了,對一些追求執行時間的acmer們很不方便,這裡記錄幾種方式自己進行處理。include include int main dword end ti...

abap執行時間

abap 4的程式會需要花費大量的時間執行,而且會使其它程序被迫暫停以等待當前程式執行結束。這裡提供一些建議以提高你的程式執行速度和系統荷載。1.最主要的是儘量減少i o操作,然後是記憶體占用,在再就是cpu的負載。類似對硬碟的讀寫的i o操作是最耗費時間的。如果對記憶體的操作不加以控制,可能有些時...