pbds tree 使用擴展——2025.7.7 鮮花
pbds tree 使用擴展
白鳥過河灘
風把我不想知道的事情
告訴我
河把我不想忘記的故事
也帶走
我摘下我的翅膀
它變成白鳥
白鳥我的白鳥 逆著風去吧
飛過河灘 揮一揮 一去不回還
一去不回還 風起水起難靠岸
白鳥白鳥不要回頭望
你要替我飛去那地方
一去那地方 那是你我共同故鄉
抓住和抓不住的照片 哪張更美
去過和沒去過的地方 哪里更遠
白鳥我的白鳥
你要飛得更高不要回來
若還想與我相見
就來我的夢里邊
白鳥過河灘
揮一揮 一去不回還
一去不回還 風起水起難靠岸
白鳥白鳥不要回頭望
你要替我飛去那地方
一去那地方 那是你我共同故鄉
別回來 我將終究順流入大海
順流入大海
海不問我從何處來
長風長風飄在山海間
白鳥白鳥展翅入蒼天
一去入蒼天 蒼天遠在海背面
一去入蒼天 蒼天遠比海更遠

震驚,tree 的 split 和 join 竟然是 \(\mathcal{O}(n)\) 的
標題黨一下,但我測出來就是這樣的,詳見 這個討論,有沒有人能回答一下 QwQ。
前兩天閑的沒事整的。
看一下其定義:
__gnu_pbds::tree<Key, Mapped, Cmp_Fn = std::less<Key>, Tag = rb_tree_tag,
Node_Update = null_tree_node_update,
Allocator = std::allocator<char>>
Key 是值的類型,Mapped 是映射到的值的類型,類似 map 的第二個參數,沒有可以寫 null_type,Tag 是平衡樹類型,正常人不會用其他的,Node_Update 是節點合并方式,等下講,Allocator 是內存池。
Node_Update 內置的有兩個,一個是 null_tree_node_update 表示啥都不干,另一個是 tree_order_statistics_node_update 表示維護子樹大小,支持 order_of_key(x) 和 find_by_order(x) 方法。
我們想盡可能的自定義這個,就白嫖一個紅黑樹和內存管理的板子。
于是我們自定義 Node_Update:
大致結構如下:
template <class cIt, class It, class Cmp, class _A> struct Up{
typedef int metadata_type;
virtual cIt node_begin() const = 0;
virtual cIt node_end() const = 0;
void operator()(It t, cIt end){}
};
這里的 metadata_type 是維護的附加信息的類型,像是什么都行,這里暫時先用 int 代替。
cIt 是 const 類型封裝指針,It 是封裝指針,Cmp 是比較方法,_A 是內存池。
pbds 的 tree 的指針封了很多層,這里簡單說一下:
一般用 lower_bound 之類的方法得到的是值指針,其解引用為節點的值。
這里傳入的 cIt 和 It 的指針是封裝指針,其把節點指針封裝了一下。
節點指針是指向平衡樹上一個節點的指針,我們可以用 .m_p_nd 獲得一個值指針或一個封裝指針的節點指針。節點指針有如下幾個比較常用的成員:
m_p_left 其左二子的指針,m_p_right 其右兒子的指針,m_p_parent 其父親指針,m_metadata 其附加信息的值,其解引用為節點的值。
注意 m_metadata 要修改要強轉成 metadata_type&。
封裝指針既然是其封裝,則一定有一些函數來調用:.get_l_child() 獲得左兒子,.get_r_child() 獲得右兒子,.get_metadata() 獲得附加值,其解引用為節點的值。注意到其沒有封裝父指針。
我們拋棄這些封裝,直接用節點指針即可。
函數 node_begin() 返回根節點的封裝指針,node_end() 返回空節點(即葉子節點的兒子)的封裝指針。
operator() 是合并方式,將 t 的左右節點合并,end 和 node_end() 是一樣的,可以直接不管。
于是給出如下宏定義:
#define Fm(t) ((t).m_p_nd)
#define lson (Fm(t)->m_p_left)
#define rson (Fm(t)->m_p_right)
#define Dt(t) ((t) == node_end() ? 0 : Fm(t)->m_metadata)
#define Cg(t) (int&)(Fm(t)->m_metadata)
Fm 表示獲得其節點指針,lson 和 rson 是其左右兒子,Dt 是獲得值,Cg是修改值。
還可以在 Up 中寫些函數,最后會是 tree 的成員函數,類似 order_of_key(x) 和 find_by_order(x)。
我們寫一個節點類:
struct Nd{ int v; mutable int c; bool operator<(const Nd &_) const { return v < _.v; } };
為了方便以后搞事,我們直接將 tree 繼承下來:
struct Rbt : private __gnu_pbds::tree<Nd, __gnu_pbds::null_type, less<Nd>, __gnu_pbds::rb_tree_tag, Up>{}
然后我們可以在里面改 insert 和 erase 之類的。
用 update_to_top(Fm(t), (node_update*)this) 可以直接 updata 到頂。
pushdown 就要自己在 Up 里寫一個了。
于是我們基本能實現平衡樹所有功能了(除了分裂合并)。
這里給一個 P6136 【模板】普通平衡樹(數據加強版) 的代碼:
Code
/*
ulimit -s 128000 && clear && g++ % -o %< -O2 -std=c++14 -DLOCAL -Wall -Wextra && time ./%< && size %<
ulimit -s 128000 && clear && g++ % -o %< -O2 -std=c++14 -DLOCAL -Wall -Wextra -fsanitize=address,undefined -g && time ./%< && size %<
echo && cat out.out && echo
*/
#include <bits/stdc++.h>
#include <bits/extc++.h>
using namespace std;
using llt = long long;
using llf = long double;
using ull = unsigned long long;
#define endl '\n'
#ifdef LOCAL
FILE *InFile = freopen("in.in", "r", stdin), *OutFile = freopen("out.out", "w", stdout);
#endif
struct Nd{ int v; mutable int c; bool operator<(const Nd &_) const { return v < _.v; } };
#define Fm(t) ((t).m_p_nd)
template <class cIt, class It, class Cmp, class _A> struct Up{
typedef int metadata_type;
#define lson (Fm(t)->m_p_left)
#define rson (Fm(t)->m_p_right)
#define Dt(t) ((t) == node_end() ? 0 : Fm(t)->m_metadata)
#define Cg(t) (int&)(Fm(t)->m_metadata)
virtual cIt node_begin() const = 0;
virtual cIt node_end() const = 0;
void operator()(It t, cIt end){
It ls = lson, rs = rson;
int s = 0;
if(ls != end) s += Dt(ls);
if(rs != end) s += Dt(rs);
Cg(t) = s + (*t)->c;
}
int kth(int k){
cIt t = node_begin(), _ = node_end();
while(t != _){
cIt ls = lson, rs = rson; int s = Dt(ls);
if(k <= s) t = ls;
else if(k <= s + (*t)->c) return (*t)->v;
else k -= s + (*t)->c, t = rs;
}
return -1;
}
int rnk(int v){
cIt t = node_begin(), _ = node_end(); int r = 1;
while(t != _){
cIt ls = lson, rs = rson;
if(v <= (*t)->v) t = ls;
else r += (*t)->c + Dt(ls), t = rs;
}
return r;
}
#undef lson
#undef rson
#undef Dt
#undef Cg
};
struct Rbt : private __gnu_pbds::tree<Nd, __gnu_pbds::null_type, less<Nd>, __gnu_pbds::rb_tree_tag, Up>{
#define Upd(t) update_to_top(Fm(t), (node_update*)this)
void Ins(int v){
auto k = lower_bound({v, 0});
if(k != end() && k->v == v) ++k->c, Upd(k);
else insert({v, 1});
}
void Era(int v){
auto k = lower_bound({v, 0});
if(k != end() && k->c > 1) --k->c, Upd(k);
else erase(k);
}
int Rnk(int v){ return rnk(v); }
int Kth(int k){ return kth(k); }
int Pre(int v){ return (--lower_bound({v, 0}))->v; }
int Nxt(int v){ return (upper_bound({v, 0}))->v; }
#undef Upd
} rbt;
#undef Fm
int n, m, lans, aas;
int main(){
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> m;
for(int i = 1, v; i <= n; ++i) cin >> v, rbt.Ins(v);
for(int i = 1; i <= m; ++i){
int op, v; cin >> op >> v, v ^= lans;
if(op == 1) rbt.Ins(v);
else if(op == 2) rbt.Era(v);
else if(op == 3) aas ^= lans = rbt.Rnk(v);
else if(op == 4) aas ^= lans = rbt.Kth(v);
else if(op == 5) aas ^= lans = rbt.Pre(v);
else aas ^= lans = rbt.Nxt(v);
}
cout << aas;
}
P





本文來自博客園,作者:xrlong,轉載請注明原文鏈接:http://www.rzrgm.cn/xrlong/p/18971132
版權聲明:本作品采用 「署名-非商業性使用-相同方式共享 4.0 國際」許可協議(CC BY-NC-SA 4.0) 進行許可。

浙公網安備 33010602011771號