数据结构:树(平铺模式)。
-
数据类型
类型设定
size_type = uint32_t
,表示树中编号的类型。模板参数
typename Tp
,表示树中边权的类型。默认值为bool
,表示边不带权值。模板参数
size_type MAX_VERTEX
,表示结点总数的上限。构造参数
size_type vertex_cnt
,表示本树具体的结点数。默认为0
。 -
时间复杂度
$O(1)$ 。 -
备注
本模板和
VectorTree
,LinkTree
模板一样都是树的存储模板。与VectorTree
相比,本树没有使用std::vector
来存储邻接表,避免了动态扩容的开销。与LinkTree
相比,本树的邻接表保存在连续的内存上,因此在访问邻接表时速度更快。相应的,本树的缺点为,在加入所有边之后,须调用prepare
进行预处理,方可让树进入可用状态,如果之后并没有对邻接表进行大量访问的话,预处理的开销就得不偿失。显然,添加的边数必须恰比点数少一。
注意:
以下各种方法均要求结点编号从
0
开始。
-
数据类型
输入参数
size_type vertex_cnt
,表示树中的点数。 -
时间复杂度
$O(1)$ 。 -
备注
调用本方法后,树被清空,成为新的大小的空树,等待加边。
-
数据类型
输入参数
size_type a
,表示边的一端的结点编号。输入参数
size_type b
,表示边的另一端的结点编号。输入参数
Tp dis
,表示边的权值大小。默认为Tp
类的默认值。 -
时间复杂度
$O(1)$ 。 -
备注
在添加边时,不关注哪端在上哪端在下。
在无权值树中,不需要传递第三个参数。
-
数据类型
-
时间复杂度
$O(n)$ 。 -
备注
一棵树,在加边完成后,必须调用
prepare
函数才能进入预备状态,可以进行遍历、打印、以及配合其他模板。
-
数据类型
输入参数
size_type root
,表示要设置为的根结点编号。 -
时间复杂度
$O(1)$ 。 -
备注
在调用本方法前,树的根默认为
-1
。
-
数据类型
返回类型
size_type
,表示结点数量。 -
时间复杂度
$O(1)$ 。
-
数据类型
输入参数
size_type a
,表示当前结点。输入参数
Callback &&call
,表示要对邻接点调用的回调函数。 -
时间复杂度
$O(n)$ ,此处n
指结点a
的度。 -
备注
第二个参数
call
必须为可调用对象,入参为size_type to
,表示结点a
的邻接点。
-
数据类型
输入参数
size_type a
,表示当前结点。输入参数
Callback &&call
,表示要对邻接边调用的回调函数。 -
时间复杂度
$O(n)$ ,此处n
指结点a
的度。 -
备注
第二个参数
call
必须为可调用对象,入参为(size_type to, Tp dis)
,分别表示结点a
的邻接点以及之间的边长。
-
数据类型
输入参数
size_type a
,表示进行树形dp
时的起始结点,或者说根结点。输入参数
PreWork &&pre_work
,表示在dfs
历程中,每当来到一个结点处时,最先调用的回调函数。输入参数
Report &&report
,表示在dfs
历程中,每当来到一个结点处时,对其一个邻接点递归返回后,调用的回调函数。输入参数
AfterWork &&after_work
,表示在dfs
历程中,每当来到一个结点处时,对所有邻接点递归返回后,调用的回调函数。 -
时间复杂度
$O(n)$ 。 -
备注
在调用本方法之前,不需要先指定树的常态根,只须指定本次的临时根。
pre_work
必须为可调用对象,入参为size_type a, size_type p
,分别表示当前结点和父结点。出发点的父结点为-1
。若
pre_work
返回类型为bool
时,可以用来剪枝;若返回false
则不会在当前路径下继续dfs
,而是直接返回。report
必须为可调用对象,入参为size_type a, size_type to
,分别表示当前结点和刚完成递归的邻接点。邻接点一定不为父结点。after_work
必须为可调用对象,入参为size_type a
,表示当前结点。
-
数据类型
输入参数
size_type a
,表示进行树形dp
时的起始结点,或者说根结点。输入参数
PreWork &&pre_work
,表示在dfs
历程中,每当来到一个结点处时,最先调用的回调函数。输入参数
Report &&report
,表示在dfs
历程中,每当来到一个结点处时,对其一个邻接点递归返回后,调用的回调函数。输入参数
AfterWork &&after_work
,表示在dfs
历程中,每当来到一个结点处时,对所有邻接点递归返回后,调用的回调函数。 -
时间复杂度
$O(n)$ 。 -
备注
本方法除额外使用边的权值信息外,与
tree_dp_vertex
相同。在调用本方法之前,不需要先指定树的常态根,只须指定本次的临时根。
pre_work
必须为可调用对象,入参为size_type a, size_type p, Tp dis
,分别表示当前结点和父结点,及之间的边的权值。出发点的父结点为-1
,之间的边的权值为0
。若
pre_work
返回类型为bool
时,可以用来剪枝;若返回false
则不会在当前路径下继续dfs
,而是直接返回。report
必须为可调用对象,入参为size_type a, size_type to, Tp dis
,分别表示当前结点和刚完成递归的邻接点,及之间的边的权值。邻接点一定不为父结点。after_work
必须为可调用对象,入参为size_type a, Tp dis
,表示当前结点,及与父结点间的边的权值。
#include "IO/FastIO.h"
#include "TREE/FlatTree.h"
void test_bool_tree() {
// 一个无权树
OY::FlatTree::Tree<bool, 100000> T(6);
// 加边
T.add_edge(0, 1);
T.add_edge(0, 2);
T.add_edge(0, 3);
T.add_edge(3, 4);
T.add_edge(3, 5);
// 预备
T.prepare();
// 定根
T.set_root(0);
// 输出观察树形(粘贴到http://mshang.ca/syntree/)
cout << "T:" << T << endl;
// 邻接表的遍历
cout << "neighbors of 3:\n";
T.do_for_each_adj_vertex(3, [&](int a) {
cout << a << " is connected with 3\n";
});
// 一般情况下我们可以利用 tree_dp_vertex 快捷地进行树形 dp
// 树形 dp 求深度(需要信息从上往下传播)
std::vector<int> dep(6);
T.tree_dp_vertex(0, [&](int a, int p) { dep[a] = ~p ? dep[p] + 1 : 0; }, {}, {});
cout << "dep of tree vertexes:\n";
for (int i = 0; i < 6; i++) cout << dep[i] << (i == 5 ? '\n' : ' ');
// 树形 dp 求子树大小(需要信息从下往上传播)
std::vector<int> size(6);
T.tree_dp_vertex(
0, [&](int a, int p) { size[a] = 1; }, [&](int a, int to) { size[a] += size[to]; }, {});
cout << "size of tree vertexes:\n";
for (int i = 0; i < 6; i++) cout << size[i] << (i == 5 ? '\n' : ' ');
}
void test_int_tree() {
// 一个边权为 int 类型的树
OY::FlatTree::Tree<int, 100000> T(6);
// 加边
T.add_edge(0, 1, 100);
T.add_edge(0, 2, 200);
T.add_edge(0, 3, 50);
T.add_edge(3, 4, 110);
T.add_edge(3, 5, 80);
// 预备
T.prepare();
// 定根
T.set_root(0);
// 输出观察树形(粘贴到http://mshang.ca/syntree/)
cout << "T:" << T << endl;
// 邻接表的遍历
cout << "neighbors of 3:\n";
// 此时我们用 do_for_each_adj_edge 来遍历邻接表,不仅可以遍历邻接点,还能知道边权
// 当然如果你不需要边权信息,还想用 do_for_each_adj_vertex 也是可以的
T.do_for_each_adj_edge(3, [&](int a, int dis) {
cout << a << " is connected with 3, distance = " << dis << endl;
});
// tree_dp_vertex 仍然可用
// 这里展示使用 tree_dp_edge 来求到某点的距离
std::vector<int> distance(6);
T.tree_dp_edge(0, [&](int a, int p, int dis) { distance[a] = ~p ? distance[p] + dis : 0; }, {}, {});
cout << "distance of tree vertexes:\n";
for (int i = 0; i < 6; i++) cout << distance[i] << (i == 5 ? '\n' : ' ');
}
int main() {
test_bool_tree();
test_int_tree();
}
#输出如下
T:[0[1][2][3[4][5]]]
neighbors of 3:
0 is connected with 3
4 is connected with 3
5 is connected with 3
dep of tree vertexes:
0 1 1 1 2 2
size of tree vertexes:
6 1 1 3 1 1
T:[0[1][2][3[4][5]]]
neighbors of 3:
0 is connected with 3, distance = 50
4 is connected with 3, distance = 110
5 is connected with 3, distance = 80
distance of tree vertexes:
0 100 200 50 160 130