**:
編寫乙個資料結構在每次$o(logn)(1 \leq n \leq 1e6)$完成以下功能:
一、插入乙個數到序列中;二、在序列中刪除某乙個數;三、找到第$k$大;四、詢問第$k$大的數;五、找到$x$的前驅,六、找到$x$的後繼。
很顯然,二叉搜尋樹就可以完成這個任務,不過最壞情況下,二叉樹會退化成鏈,就會超時,因此我們就需要平衡二叉樹,其中較為容易編寫,速度又快的是$treap$。$treap$分為有旋$treap$和無旋$treap$,前者速度快,但不支援持久化,後者速度慢,但支援持久化。
treap的節點的權值維護了二叉樹的性質,為了平衡,就需要通過另外乙個數維護堆性質使其平衡,這個數就是每乙個節點對應的隨機數。旋轉的時候按照隨機數進行旋轉,分為左旋和右旋,如圖:
(參考部落格:
左旋和右旋
顯然,右旋就是原樹根的左子樹變成樹根,然後原樹根的左子樹的右子樹變成原樹根的左子樹,左旋同理。易證旋轉後二叉樹性質不變。
插入時,先找到插入點,然後回溯時旋轉。
刪除較為複雜,刪除時,先找到刪除點,然後觀察子樹的隨機數值選擇乙個子樹作為刪除後的樹根,然後通過旋轉把需要刪除的節點移動到葉子後刪除。
查詢操作易於理解,看**即可。
ac**:
#include using namespace std;const int maxn=100005;
const int inf=0x3f3f3f3f;
struct treap
; int cnt=0;
node tr[maxn];
void init()
int _rand()
void pushup(int p)
void right(int &k)
void left(int &k)
void insert(int &p,int x)
++tr[p].size;
if(x==tr[p].val)
++tr[p].num;
else if(xtr[p].val)
}else if(tr[p].val=rnk)
return querynum(tr[p].lc,rnk);
rnk-=tr[tr[p].lc].size;
if(rnk<=tr[p].num)
return tr[p].val;
rnk-=tr[p].num;
return querynum(tr[p].rc,rnk);
}int queryfront(int &p,int x)
int queryback(int &p,int x)
};int pos;
treap tr;
int main()
; node tr[maxn];
int cnt=0;
void init()
void pushup(int p)
int _rand()
void merge(int &rt,int a,int b)//保證a的權值小於b的權值
if(tr[a].rnk
洛谷P3369 模板 普通平衡樹
本蒟蒻最近剛剛學會平衡樹,特來寫篇部落格以加深印象。我的意思是若寫的不好望各位奆佬多多包含!插入 x 數 刪除 x 數 若有多個相同的數,因只刪除乙個 查詢 x 數的排名 排名定義為比當前數小的數的個數 1 若有多個相同的數,因輸出最小的排名 查詢排名為 x 的數 求 x 的前驅 前驅定義為小於 x...
洛谷P3369 模板 普通平衡樹
插入x數 刪除x數 若有多個相同的數,因只刪除乙個 查詢x數的排名 排名定義為比當前數小的數的個數 1。若有多個相同的數,因輸出最小的排名 查詢排名為x的數 求x的前驅 前驅定義為小於x,且最大的數 求x的後繼 後繼定義為大於x,且最小的數 輸入格式 第一行為n,表示操作的個數,下面n行每行有兩個數...
洛谷P3369 模板 普通平衡樹 Treap
插入xxx數 刪除x xx數 若有多個相同的數,因只刪除乙個 查詢x xx數的排名 排名定義為比當前數小的數的個數 1 1 1。若有多個相同的數,因輸出最小的排名 查詢排名為x xx的數 求x xx的前驅 前驅定義為小於x xx,且最大的數 求x xx的後繼 後繼定義為大於x xx,且最小的數 總算...