pbds tree 使用扩展——2025.7.7 鲜花

pbds tree 使用扩展

白鸟过河滩
风把我不想知道的事情
告诉我
河把我不想忘记的故事
也带走
我摘下我的翅膀
它变成白鸟
白鸟我的白鸟 逆着风去吧
飞过河滩 挥一挥 一去不回还
一去不回还 风起水起难靠岸
白鸟白鸟不要回头望
你要替我飞去那地方
一去那地方 那是你我共同故乡
抓住和抓不住的照片 哪张更美
去过和没去过的地方 哪里更远
白鸟我的白鸟
你要飞得更高不要回来
若还想与我相见
就来我的梦里边
白鸟过河滩
挥一挥 一去不回还
一去不回还 风起水起难靠岸
白鸟白鸟不要回头望
你要替我飞去那地方
一去那地方 那是你我共同故乡
别回来 我将终究顺流入大海
顺流入大海
海不问我从何处来
长风长风飘在山海间
白鸟白鸟展翅入苍天
一去入苍天 苍天远在海背面
一去入苍天 苍天远比海更远

震惊,treesplitjoin 竟然是 \(\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_typeTag 是平衡树类型,正常人不会用其他的,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 代替。

cItconst 类型封装指针,It 是封装指针,Cmp 是比较方法,_A 是内存池。

pbds 的 tree 的指针封了很多层,这里简单说一下:

一般用 lower_bound 之类的方法得到的是值指针,其解引用为节点的值。

这里传入的 cItIt 的指针是封装指针,其把节点指针封装了一下。

节点指针是指向平衡树上一个节点的指针,我们可以用 .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 的左右节点合并,endnode_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 表示获得其节点指针,lsonrson 是其左右儿子,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>{}

然后我们可以在里面改 inserterase 之类的。

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





posted @ 2025-07-07 19:01  xrlong  阅读(88)  评论(0)    收藏  举报

Loading