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,转载请注明原文链接:https://www.cnblogs.com/xrlong/p/18971132
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。