TREE
建中 225 賴柏宇
海之音 / 小海 或其他類似的
表達能力差,不懂要問
INFOR 36th 學術長
不會樹論所以來當樹論講師
#include <iostream>
#include <vector>
#incldue <functional>
#incldue <utility>
// ...可能有其他的
int n; // 點數
int m; // 邊數
int cur; // 當前節點編號
int pre; // 上一個節點,通常是父節點
int nxt; // 下一個節點,通常是子節點
int depth; // 深度
int size; // 大小,通常是子樹大小
const int null = -1;
const int MOD = 1e9 + 7;
const int INF = 2e9;
template <typename T>
using vec = std::vector<T>;
using pii = std::pair<int, int>;
using std::min;
using std::max;
using std::cin;
using std::cout;
註:編譯器使用 g++ , std=c++20
Introduction
樹是一種無向圖,其中任意兩個頂點間存在唯一一條路徑。
或者說,只要沒有環的連通圖就是樹。
– Wikipedia
1
0
2
3
4
5
6
7
8
9
1
0
2
3
4
5
6
7
8
9
1
0
2
3
4
5
6
7
8
9
1
0
2
3
4
5
6
7
8
9
1
0
2
3
4
5
6
7
8
9
d = 0
d = 1
d = 2
d = 3
1
0
2
3
4
5
6
7
8
9
height = 4
1
0
2
3
4
5
6
7
8
9
1
0
2
3
4
5
6
7
8
9
1
0
2
3
4
5
6
7
8
9
0
3
9
1
4
5
2
6
7
8
struct Edge {
// edge data
};
struct Node {
// node data
vec<Edge> edges;
};
using Tree = vec<Node>;
// 或者如果你想用 for (auto &nxt : tree[cur]) 遍歷
struct Node {
// node data
vec<Edge> edges;
auto begin() {
return edges.begin();
}
auto end() {
return edges.end();
}
};
DFS on Tree
1
0
2
1
0
2
i
當前拜訪節點
i
前個拜訪節點
# 一般圖 DFS
# 樹上的 DFS
1
0
2
1
0
2
i
當前拜訪節點
i
前個拜訪節點
# 一般圖 DFS
# 樹上的 DFS
1
0
2
1
0
2
i
當前拜訪節點
i
前個拜訪節點
# 一般圖 DFS
# 樹上的 DFS
1
0
2
1
0
2
i
當前拜訪節點
i
前個拜訪節點
# 一般圖 DFS
# 樹上的 DFS
1
0
2
1
0
2
i
當前拜訪節點
i
前個拜訪節點
# 一般圖 DFS
# 樹上的 DFS
拜訪重複節點
1
0
2
1
0
2
i
當前拜訪節點
i
前個拜訪節點
# 一般圖 DFS
# 樹上的 DFS
1
0
2
1
0
2
i
當前拜訪節點
i
前個拜訪節點
# 一般圖 DFS
# 樹上的 DFS
1
0
2
1
0
2
i
當前拜訪節點
i
前個拜訪節點
# 一般圖 DFS
# 樹上的 DFS
1
0
2
1
0
2
i
當前拜訪節點
i
前個拜訪節點
# 一般圖 DFS
# 樹上的 DFS
void dfs(int cur, int pre, vec<vec<int>> &tree) {
for (int &nxt : tree[cur]) {
if (nxt == pre) continue;
dfs(nxt, cur, tree);
}
}
處理順序
i
當前拜訪節點
i
已拜訪節點
1
3
0
2
7
5
4
6
#遍歷順序
處理順序
i
當前拜訪節點
i
已拜訪節點
1
3
0
2
7
5
4
6
#遍歷順序
處理順序
i
當前拜訪節點
i
已拜訪節點
1
3
0
2
7
5
4
6
#遍歷順序
處理順序
i
當前拜訪節點
i
已拜訪節點
1
3
0
2
7
5
4
6
#遍歷順序
處理順序
i
當前拜訪節點
i
已拜訪節點
1
3
0
2
7
5
4
6
#遍歷順序
處理順序
i
當前拜訪節點
i
已拜訪節點
1
3
0
2
7
5
4
6
#遍歷順序
處理順序
i
當前拜訪節點
i
已拜訪節點
1
3
0
2
7
5
4
6
#遍歷順序
處理順序
i
當前拜訪節點
i
已拜訪節點
1
3
0
2
7
5
4
6
#遍歷順序
處理順序
i
當前拜訪節點
i
已拜訪節點
1
3
0
2
7
5
4
6
#遍歷順序
處理順序
i
當前拜訪節點
i
已拜訪節點
1
3
0
2
7
5
4
6
#遍歷順序
處理順序
i
當前拜訪節點
i
已拜訪節點
1
3
0
2
7
5
4
6
#遍歷順序
處理順序
i
當前拜訪節點
i
已拜訪節點
1
3
0
2
7
5
4
6
#遍歷順序
處理順序
i
當前拜訪節點
i
已拜訪節點
1
3
0
2
7
5
4
6
#遍歷順序
處理順序
i
當前拜訪節點
i
已拜訪節點
1
3
0
2
7
5
4
6
#遍歷順序
處理順序
i
當前拜訪節點
i
已拜訪節點
1
3
0
2
7
5
4
6
#遍歷順序
處理順序
i
當前拜訪節點
i
已拜訪節點
1
3
0
2
7
5
4
6
#遍歷順序
處理順序
i
當前拜訪節點
i
已拜訪節點
1
3
0
2
7
5
4
6
#遍歷順序
#define lchild first
#define rchild second
void preorder(int cur, vec<pii> &tree) {
if (cur == null) return;
process(cur);
preorder(tree[cur].lchild, tree);
preorder(tree[cur].rchild, tree);
}
void inorder(int cur, vec<pii> &tree) {
if (cur == null) return;
inorder(tree[cur].lchild, tree);
process(cur);
inorder(tree[cur].rchild, tree);
}
void postorder(int cur, vec<pii> &tree) {
if (cur == null) return;
postorder(tree[cur].lchild, tree);
postorder(tree[cur].rchild, tree);
process(cur);
}
#include <iostream>
#include <utility>
#include <vector>
#define lchild first
#define rchild second
template <typename T>
using vec = std::vector<T>;
using pii = std::pair<int, int>;
const int null = -1;
void process(int node) {
std::cout << node << " ";
}
void preorder(int cur, vec<pii> &tree) {
if (cur == null) return;
process(cur);
preorder(tree[cur].lchild, tree);
preorder(tree[cur].rchild, tree);
}
void inorder(int cur, vec<pii> &tree) {
if (cur == null) return;
inorder(tree[cur].lchild, tree);
process(cur);
inorder(tree[cur].rchild, tree);
}
void postorder(int cur, vec<pii> &tree) {
if (cur == null) return;
postorder(tree[cur].lchild, tree);
postorder(tree[cur].rchild, tree);
process(cur);
}
int main() {
vec<pii> tree = {{-1, -1}, {0, 2}, {-1, -1}, {1, 7}, {-1, -1}, {4, 6}, {-1, -1}, {5, -1}};
int root = 3;
preorder(root, tree);
std::cout << "\n";
inorder(root, tree);
std::cout << "\n";
postorder(root, tree);
std::cout << "\n";
}
3 1 0 2 7 5 4 6
0 1 2 3 4 5 6 7
0 2 1 4 6 5 7 3
i
LIS 所在節點
LIS 所在分支
i
1
3
2
7
5
4
6
0
#樹上LIS
1
3
0
2
7
5
4
6
可能的尾端
[...]
[3]
[1]
[0]
[1, 2]
[3, 7]
[3, 5]
[3, 4]
[3, 4, 6]
#樹上LIS
1
3
0
2
7
5
4
6
當前節點可能的尾端
[...]
[]
i
當前拜訪節點
#樹上LIS
1
3
0
2
7
5
4
6
當前節點可能的尾端
[...]
[3]
i
當前拜訪節點
#樹上LIS
1
3
0
2
7
5
4
6
當前節點可能的尾端
[...]
[1]
i
當前拜訪節點
#樹上LIS
1
3
0
2
7
5
4
6
當前節點可能的尾端
[...]
[0]
i
當前拜訪節點
#樹上LIS
1
3
0
2
7
5
4
6
當前節點可能的尾端
[...]
[1]
i
當前拜訪節點
#樹上LIS
1
3
0
2
7
5
4
6
當前節點可能的尾端
[...]
[1, 2]
i
當前拜訪節點
#樹上LIS
1
3
0
2
7
5
4
6
當前節點可能的尾端
[...]
[1]
i
當前拜訪節點
#樹上LIS
1
3
0
2
7
5
4
6
當前節點可能的尾端
[...]
[3]
i
當前拜訪節點
#樹上LIS
1
3
0
2
7
5
4
6
當前節點可能的尾端
[...]
[3, 7]
i
當前拜訪節點
#樹上LIS
1
3
0
2
7
5
4
6
當前節點可能的尾端
[...]
[3, 5]
i
當前拜訪節點
#樹上LIS
1
3
0
2
7
5
4
6
當前節點可能的尾端
[...]
[3, 4]
i
當前拜訪節點
#樹上LIS
1
3
0
2
7
5
4
6
當前節點可能的尾端
[...]
[3, 5]
i
當前拜訪節點
#樹上LIS
1
3
0
2
7
5
4
6
當前節點可能的尾端
[...]
[3, 5, 6]
i
當前拜訪節點
#樹上LIS
1
3
0
2
7
5
4
6
當前節點可能的尾端
[...]
[3, 5]
i
當前拜訪節點
#樹上LIS
1
3
0
2
7
5
4
6
當前節點可能的尾端
[...]
[3, 7]
i
當前拜訪節點
#樹上LIS
1
3
0
2
7
5
4
6
當前節點可能的尾端
[...]
[3]
i
當前拜訪節點
#樹上LIS
1
3
0
2
7
5
4
6
當前節點可能的尾端
[...]
[]
i
當前拜訪節點
#樹上LIS
struct LIS {
vec<int> mono;
std::stack<pii> trace;
void add_number(int n) {
if (mono.empty() || n > mono.back()) {
mono.push_back(n);
trace.push({-1, n});
}
else {
auto pos = std::lower_bound(mono.begin(), mono.end(), n);
trace.push({pos - mono.begin(), *pos});
*pos = n;
}
}
void undo() {
auto [pos, val] = trace.top();
trace.pop();
if (pos == -1) mono.pop_back();
else mono[pos] = val;
}
};
int max_len = 0;
void tree_lis(int cur, int pre, LIS &lis, const vec<int> &nums, const vec<vec<int>> &tree) {
lis.add_number(nums[cur]);
max_len = max(max_len, lis.mono.size());
for (const int &nxt : tree[cur]) {
if (nxt == pre) continue;
tree_lis(nxt, cur, lis, nums, tree);
}
lis.undo();
}
2024 學術上機考 B2
DP on Tree
3
8
1
1
4
3
1
1
#子樹大小
vec<int> sub_sizes;
int get_sub_size(int cur, const vec<vec<int>> &tree) {
// 這題的測資輸法不會有環
for (const int &nxt : tree[cur])
sub_sizes[cur] += get_sub_size(nxt, tree);
return sub_sizes[cur] + 1; // 回傳時記得加上自己
}
int main() {
std::ios_base::sync_with_stdio(0), std::cin.tie(0);
int n;
cin >> n;
sub_sizes.resize(n, 0);
vec<vec<int>> tree(n);
for (int i = 1; i < n; i++) {
int parent;
cin >> parent;
parent--;
tree[parent].push_back(i);
}
get_sub_size(0, tree);
for (const int &sub_size : sub_sizes)
cout << sub_size << " ";
}
(0, 0)
(0, 0)
(0, 1)
(0, 0)
(0, 0)
(0, 1)
(1, 1)
(2, 3)
#(dp0, dp1)
vec<pii> dp;
// dp[i].first: 不選當前節點的最大值
// dp[i].second: 選當前節點的最大值
void max_match(int cur, int pre, const vec<vec<int>> &tree) {
for (const int &nxt : tree[cur]) {
if (nxt == pre) continue;
max_match(nxt, cur, tree);
dp[cur].first += max(dp[nxt].first, dp[nxt].second);
// 不選當前節點:所有分支的最大值和
}
for (const int &nxt : tree[cur]) {
if (nxt == pre) continue;
int nxt_ans = max(dp[nxt].first, dp[nxt].second);
dp[cur].second = max(dp[cur].first - nxt_ans + dp[nxt].first + 1, dp[cur].second);
// 選當前節點:其中一個分支必須沒有選才能接當前節點
}
}
int main() {
std::ios_base::sync_with_stdio(0), std::cin.tie(0);
int n;
cin >> n;
vec<vec<int>> tree(n);
dp.resize(n);
for (int i = 0; i < n - 1; i++) {
int u, v;
std::cin >> u >> v;
u--, v--;
tree[u].push_back(v);
tree[v].push_back(u);
}
max_match(0, null, tree);
cout << std::max(dp[0].first, dp[0].second) << "\n";
}
int sub_depth_sum(int cur, int pre, int cur_depth, const vec<vec<int>> &tree) {
int cur_depth_sum = cur_depth;
for (const int &nxt : tree[cur]) {
if (nxt == pre) continue;
cur_depth_sum += sub_depth_sum(nxt, cur, cur_depth + 1, tree);
}
return cur_depth_sum;
}
12
?
#轉移
12
?
#轉移
12
?
#轉移
#define int long long
vec<int> sub_size;
int preprocess(int cur, int pre, int cur_depth, const vec<vec<int>> &tree) {
int cur_depth_sum = cur_depth;
sub_size[cur] = 1;
for (const int &nxt : tree[cur]) {
if (nxt == pre) continue;
cur_depth_sum += preprocess(nxt, cur, cur_depth + 1, tree);
sub_size[cur] += sub_size[nxt];
}
return cur_depth_sum;
}
vec<int> depth_sums;
void trans_from_root(int cur, int pre, const vec<vec<int>> &tree) {
for (const int &nxt : tree[cur]) {
if (nxt == pre) continue;
depth_sums[nxt] = depth_sums[cur] - sub_size[nxt] + (tree.size() - sub_size[nxt]);
trans_from_root(nxt, cur, tree);
}
}
signed main() {
std::ios_base::sync_with_stdio(0), std::cin.tie(0);
int n;
cin >> n;
vec<vec<int>> tree(n);
sub_size.resize(n);
depth_sums.resize(n);
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
u--, v--;
tree[u].push_back(v);
tree[v].push_back(u);
}
depth_sums[0] = preprocess(0, 0, 0, tree);
trans_from_root(0, 0, tree);
for (const auto &sum : depth_sums)
cout << sum << " ";
}
Diameter
#樹直徑
#樹直徑
(2, 2)
(4, 3)
(1, 1)
(1, 1)
(3, 1)
(2, 2)
(1, 1)
(1, 1)
#(最長, 次長)
(2, 2)
(4, 3)
(1, 1)
(1, 1)
(3, 1)
(2, 2)
(1, 1)
(1, 1)
#(最長, 次長)
int diameter_len = 0;
int get_longest_path(int cur, int pre, const vec<vec<int>> &tree) {
int l1 = 1, l2 = 1;
for (const int &nxt : tree[cur]) {
if (nxt == pre) continue;
int nxt_longest = get_longest_path(nxt, cur, tree) + 1;
if (nxt_longest >= l1) {
l2 = l1;
l1 = nxt_longest;
}
else if (nxt_longest >= l2) {
l2 = nxt_longest;
}
}
diameter_len = max(diameter_len, l1 + l2 - 1);
return l1;
}
int main() {
std::ios_base::sync_with_stdio(0), std::cin.tie(0);
int n;
cin >> n;
vec<vec<int>> tree(n);
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
u--, v--;
tree[u].push_back(v);
tree[v].push_back(u);
}
get_longest_path(0, 0, tree);
cout << diameter_len - 1;
}
Lowest Common Ancestor
1
0
2
3
4
5
6
7
8
9
d = 0
d = 1
d = 2
d = 3
1
0
2
3
4
5
6
7
8
9
d = 0
d = 1
d = 2
d = 3
1
0
2
3
4
5
6
7
8
9
d = 0
d = 1
d = 2
d = 3
1
0
2
3
4
5
6
7
8
9
d = 0
d = 1
d = 2
d = 3
1
0
2
3
4
5
6
7
8
9
d = 0
d = 1
d = 2
d = 3
1
0
2
3
4
5
6
7
8
9
d = 0
d = 1
d = 2
d = 3
1
0
2
3
4
5
6
7
8
9
d = 0
d = 1
d = 2
d = 3
1
3
0
2
7
5
4
6
#LCA(2, 5)
1
3
0
2
7
5
4
6
#LCA(2, 5)
1
3
0
2
7
5
4
6
#LCA(2, 5)
1
3
0
2
7
5
4
6
#LCA(1, 5)
1
3
0
2
7
5
4
6
#LCA(1, 5)
1
3
0
2
7
5
4
6
#LCA(1, 5)
1
3
0
2
7
5
4
6
2 的 2 代祖先為 3
5 的 2 代祖先為 3
3 為 2, 5 共祖
#LCA(2, 5)
1
3
0
2
7
5
4
6
2 的 1 代祖先為 1
5 的 1 代祖先為 7
非共祖
#LCA(2, 5)
1
3
0
2
7
5
4
6
結束
結果為 1 (或 7) 的父節點 3
#LCA(2, 5)
namespace BinaryLifting {
vec<vec<int>> ancestors;
vec<int> depths;
void dfs(int cur, int pre, int cur_depth, const vec<vec<int>> &tree) {
depths[cur] = cur_depth;
ancestors[0][cur] = pre;
for (const int &nxt : tree[cur])
if (nxt != pre)
dfs(nxt, cur, cur_depth + 1, tree);
}
void build_ancestor_table(const vec<vec<int>> &tree) {
depths.resize(tree.size());
ancestors.resize(std::__lg(tree.size()) + 1);
for (auto &_list : ancestors) _list.resize(tree.size());
dfs(0, 0, 0, tree);
for (int i = 0; i < ancestors.size() - 1; i++)
for (int u = 0; u < tree.size(); u++)
ancestors[i + 1][u] = ancestors[i][ancestors[i][u]];
}
int LCA(int u, int v) {
if (depths[v] > depths[u]) std::swap(u, v);
for (int diff = depths[u] - depths[v], i = 0; diff; diff >>= 1, i++)
if (diff & 1)
u = ancestors[i][u];
if (u == v) return u;
for (int i = ancestors.size() - 1; i >= 0; i--)
if (ancestors[i][u] != ancestors[i][v])
u = ancestors[i][u], v = ancestors[i][v];
return ancestors[0][u];
}
}
int main() {
std::ios_base::sync_with_stdio(0), std::cin.tie(0);
int n, q;
cin >> n >> q;
vec<vec<int>> tree(n);
for (int i = 1; i < n; i++) {
int u;
cin >> u;
u--;
tree[u].push_back(i);
}
BinaryLifting::build_ancestor_table(tree);
for (int i = 0; i < q; i++) {
int u, v;
cin >> u >> v;
u--, v--;
cout << BinaryLifting::LCA(u, v) + 1 << "\n";
}
}
「我是你今晚的噩夢。」
「參加這社團就別想逃過我的魔爪。」
– Robert Endre Tarjan,
沒有說過
「為什麼,到底為什麼。」
– 維基百科上大概沒有
#LCA子樹
#LCA子樹
#LCA子樹
struct DSU {
vec<int> master;
DSU(int n)
: master(n) {
for (int i = 0; i < n; i++)
master[i] = i;
}
int find(int n) {
return (master[n] == n) ? n : (master[n] = find(master[n]));
}
void combine(int a, int b) {
a = find(a), b = find(b);
master[b] = a;
}
};
當前拜訪節點
LCA
在離開一點後合併該點和它的父節點
目標:到 B 點時記錄 A 所在子樹的根
B
A
i
LCA 目標節點
#LCA(A, B)
當前拜訪節點
i
LCA 目標節點
LCA
在離開一點後合併該點和它的父節點
目標:到 B 點時記錄 A 所在子樹的根
B
A
#LCA(A, B)
當前拜訪節點
LCA
在離開一點後合併該點和它的父節點
目標:到 B 點時記錄 A 所在子樹的根
B
A
i
LCA 目標節點
#LCA(A, B)
當前拜訪節點
LCA
在離開一點後合併該點和它的父節點
目標:到 B 點時記錄 A 所在子樹的根
B
A
i
LCA 目標節點
#LCA(A, B)
當前拜訪節點
LCA
在離開一點後合併該點和它的父節點
目標:到 B 點時記錄 A 所在子樹的根
A
B
i
LCA 目標節點
#LCA(A, B)
當前拜訪節點
LCA
在離開一點後合併該點和它的父節點
目標:到 B 點時記錄 A 所在子樹的根
A
B
i
LCA 目標節點
#LCA(A, B)
當前拜訪節點
LCA
在離開一點後合併該點和它的父節點
目標:到 B 點時記錄 A 所在子樹的根
A
B
i
LCA 目標節點
#LCA(A, B)
當前拜訪節點
LCA
到達 B 節點
此時從 A 向上找(合併)
A
B
i
LCA 目標節點
#LCA(A, B)
當前拜訪節點
LCA
A
B
i
LCA 目標節點
到達 B 節點
此時從 A 向上找(合併)
#LCA(A, B)
當前拜訪節點
LCA
A
B
i
LCA 目標節點
到達 B 節點
此時從 A 向上找(合併)
因此結果就是 A 此時指向的點
#LCA(A, B)
當前拜訪節點
LCA
i
LCA 目標節點
再示範一個 A' B'
A'
B'
#LCA(A', B')
當前拜訪節點
LCA
i
LCA 目標節點
在離開一點後合併該點和它的父節點
目標:到 B' 點時記錄 A' 所在子樹的根
A'
B'
#LCA(A', B')
當前拜訪節點
LCA
i
LCA 目標節點
在離開一點後合併該點和它的父節點
目標:到 B' 點時記錄 A' 所在子樹的根
A'
B'
#LCA(A', B')
當前拜訪節點
LCA
i
LCA 目標節點
在離開一點後合併該點和它的父節點
目標:到 B' 點時記錄 A' 所在子樹的根
A'
B'
#LCA(A', B')
當前拜訪節點
LCA
i
LCA 目標節點
在離開一點後合併該點和它的父節點
目標:到 B' 點時記錄 A' 所在子樹的根
A'
B'
#LCA(A', B')
當前拜訪節點
LCA
i
LCA 目標節點
到達 B' 節點
此時從 A' 向上找(合併)
A'
B'
#LCA(A', B')
當前拜訪節點
LCA
i
LCA 目標節點
到達 B' 節點
此時從 A' 向上找(合併)
因此結果就是 A' 此時指向的點
A'
B'
#LCA(A', B')
namespace Tarjan_LCA {
struct Node {
vec<int> neighbors;
vec<pii> LCA_target; // LCA partner, index in query queue
};
vec<Node> tree;
vec<bool> visited;
vec<int> master;
vec<int> LCAs;
int root;
void set_size(int n, int q) {
tree.resize(n);
visited.resize(n, false);
master.resize(n);
LCAs.resize(q);
for (int i = 0; i < n; i++)
master[i] = i;
}
void set_root(int index) {
root = index;
}
void add_edge(int u, int v) {
tree[u].neighbors.push_back(v);
tree[v].neighbors.push_back(u);
}
void add_query(int u, int v) {
static int cnt = 0;
tree[u].LCA_target.push_back({v, cnt});
tree[v].LCA_target.push_back({u, cnt});
++cnt;
}
void solve() {
auto find = [&](int n, auto &&find) -> int {
return (n == master[n]) ? n : (master[n] = find(master[n], find));
};
auto combine = [&](int a, int b) -> void {
a = find(a, find), b = find(b, find);
if (a == b) return;
master[b] = a;
};
auto dfs = [&](int cur, int pre, auto &&dfs) -> void {
visited[cur] = true;
for (const auto &[target, index] : tree[cur].LCA_target)
if (visited[target])
LCAs[index] = find(target, find);
for (const auto &nxt : tree[cur].neighbors) {
if (nxt == pre) continue;
dfs(nxt, cur, dfs);
}
combine(pre, cur);
};
dfs(root, root, dfs);
}
} // namespace Tarjan_LCA
int main() {
std::ios_base::sync_with_stdio(0), std::cin.tie(0);
int n, q;
cin >> n >> q;
Tarjan_LCA::set_size(n, q);
Tarjan_LCA::set_root(0);
for (int u = 1; u < n; u++) {
int v;
cin >> v;
v--;
Tarjan_LCA::add_edge(u, v);
}
for (int i = 0; i < q; i++) {
int u, v;
cin >> u >> v;
u--, v--;
Tarjan_LCA::add_query(u, v);
}
Tarjan_LCA::solve();
for (const auto &ans : Tarjan_LCA::LCAs)
cout << ans + 1 << "\n";
}
Tarjan LCA 真的很美,這邊寫長只是因為方便當模板
Tree Serialization
(3, 4)
(5, 6)
(2, 7)
(10, 11)
(12, 13)
(9, 14)
(8, 15)
(1, 16)
#(入點, 出點)
(3, 4)
(5, 6)
(2, 7)
(10, 11)
(12, 13)
(9, 14)
(8, 15)
(1, 16)
#(入點, 出點)
(3, 4)
(5, 6)
(2, 7)
(10, 11)
(12, 13)
(9, 14)
(8, 15)
(1, 16)
#(入點, 出點)
(3, 4)
(5, 6)
(2, 7)
(10, 11)
(12, 13)
(9, 14)
(8, 15)
(1, 16)
#(入點, 出點)
1
2
3
4
5
6
7
8
#入點時間戳
struct Node {
vec<int> neighbors;
int start, end;
};
vec<int> serialized_tree;
void serialize(int cur, int pre, const vec<Node> &tree) {
static int time = 0;
serialized_tree[time] = cur;
tree[cur].start = time++;
for (const int &nxt : tree[cur].neighbor) {
if (nxt == pre) continue;
serialize(nxt, cur, tree);
}
serialized_tree[time] = cur;
tree[cur].end = time++;
}
struct Node {
vec<int> neighbors;
int start, end;
};
vec<int> serialized_tree;
void serialize(int cur, int pre, const vec<Node> &tree) {
static int time = 0;
serialized_tree[time] = cur;
tree[cur].start = time++;
for (const int &nxt : tree[cur].neighbor) {
if (nxt == pre) continue;
serialize(nxt, cur, tree);
}
tree[cur].end = time;
}
(3, 4)
(4, 5)
(2, 5)
(7, 8)
(8, 9)
(6, 9)
(5, 9)
(1, 9)
#(入點, 出點)
struct Node {
vec<int> neighbors;
int start, end;
int val;
};
vec<Node> tree;
vec<int> serialized_tree;
void serialize(int cur, int pre) {
static int time = 0;
serialized_tree[time] = cur;
tree[cur].start = time++;
for (const int &nxt : tree[cur].neighbors) {
if (nxt != pre)
serialize(nxt, cur);
}
tree[cur].end = time;
}
struct Stree {
vec<int> data;
int size;
Stree() {
size = tree.size();
data.resize(size << 1);
for (int i = 0; i < size; i++)
data[i + size] = tree[serialized_tree[i]].val;
for (int i = size - 1; i > 0; i--)
data[i] = data[i << 1] + data[i << 1 | 1];
}
void modify(int node, int new_val) {
int pos = tree[node].start;
pos += size;
data[pos] = new_val;
for (; pos > 1; pos >>= 1)
data[pos >> 1] = data[pos] + data[pos ^ 1];
}
int query(int root) {
int l = tree[root].start, r = tree[root].end;
int result = 0;
for (l += size, r += size; l < r; l >>= 1, r >>= 1) {
if (l & 1) result += data[l++];
if (r & 1) result += data[--r];
}
return result;
}
};
signed main() {
std::ios_base::sync_with_stdio(0), std::cin.tie(0);
int n, q;
cin >> n >> q;
tree.resize(n);
serialized_tree.resize(n);
for (int i = 0; i < n; i++)
cin >> tree[i].val;
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
u--, v--;
tree[u].neighbors.push_back(v);
tree[v].neighbors.push_back(u);
}
serialize(0, 0);
Stree stree;
for (int i = 0; i < q; i++) {
int t;
cin >> t;
if (t == 1) {
int s, x;
cin >> s >> x;
s--;
stree.modify(s, x);
}
else {
int s;
cin >> s;
s--;
cout << stree.query(s) << "\n";
}
}
}
1
2
3
4
5
6
7
8
#路徑和
1
2
3
4
5
6
7
8
#根到點路徑和
1
2
3
4
5
6
7
8
#根到點路徑和
1
2
3
4
5
6
7
8
#根到點路徑和
(3, -3)
(4, -4)
(2, -2)
(7, -7)
(8, -8)
(6, -6)
(5, -5)
(1, -1)
#(入點, 出點資料)
d(root, A)
root
LCA
A
B
#路徑和求法
d(root, A) + d(root, B)
root
LCA
A
B
#路徑和求法
d(root, A) + d(root, B) - 2 * d(root, LCA(A, B))
root
LCA
A
B
#路徑和求法
d(root, A) + d(root, B) - 2 * d(root, LCA(A, B)) + val(LCA(A, B))
root
LCA
A
B
#路徑和求法
const int root = 0;
struct Node {
vec<int> neighbors;
int start, end;
int val;
};
vec<Node> tree;
vec<int> serialized_tree;
void serialize(int cur, int pre) {
static int time = 0;
serialized_tree[time] = tree[cur].val;
tree[cur].start = time++;
for (const int &nxt : tree[cur].neighbors) {
if (nxt != pre)
serialize(nxt, cur);
}
serialized_tree[time] = -tree[cur].val;
tree[cur].end = time++;
}
struct Stree {
vec<int> data;
int size;
Stree() {
size = tree.size() << 1;
data.resize(size << 1);
for (int i = 0; i < size; i++)
data[i + size] = serialized_tree[i];
for (int i = size - 1; i > 0; i--)
data[i] = data[i << 1] + data[i << 1 | 1];
}
void modify(int node, int new_val) {
int pos = tree[node].start;
pos += size;
data[pos] = new_val;
for (; pos > 1; pos >>= 1)
data[pos >> 1] = data[pos] + data[pos ^ 1];
pos = tree[node].end;
pos += size;
data[pos] = -new_val;
for (; pos > 1; pos >>= 1)
data[pos >> 1] = data[pos] + data[pos ^ 1];
}
int query(int node) {
int l = tree[root].start, r = tree[node].start + 1;
int result = 0;
for (l += size, r += size; l < r; l >>= 1, r >>= 1) {
if (l & 1) result += data[l++];
if (r & 1) result += data[--r];
}
return result;
}
};
signed main() {
std::ios_base::sync_with_stdio(0), std::cin.tie(0);
int n, q;
cin >> n >> q;
tree.resize(n);
serialized_tree.resize(n << 1);
for (int i = 0; i < n; i++)
cin >> tree[i].val;
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
u--, v--;
tree[u].neighbors.push_back(v);
tree[v].neighbors.push_back(u);
}
serialize(root, root);
Stree stree;
for (int i = 0; i < q; i++) {
int t;
cin >> t;
if (t == 1) {
int s, x;
cin >> s >> x;
s--;
stree.modify(s, x);
}
else {
int s;
cin >> s;
s--;
cout << stree.query(s) << "\n";
}
}
}
1
0
2
序列:
#LCA 樹壓平
1
0
2
序列:0
#LCA 樹壓平
1
0
2
序列:0, 1
#LCA 樹壓平
1
0
2
序列:0, 1, 0
#LCA 樹壓平
1
0
2
序列:0, 1, 0, 2
#LCA 樹壓平
1
0
2
序列:0, 1, 0, 2, 0
#LCA 樹壓平
1
0
2
序列:0, 1, 0, 2, 0
#LCA 樹壓平
不排除是我 Tarjan 寫太爛
struct Node {
vec<int> neighbors;
int start, depth;
};
vec<Node> tree;
vec<pii> serialized_tree;
void serialize(int cur, int pre, int depth) {
static int time = 0;
serialized_tree[time] = {depth, cur};
tree[cur].start = time++;
for (const int &nxt : tree[cur].neighbors) {
if (nxt == pre) continue;
serialize(nxt, cur, depth + 1);
serialized_tree[time++] = {depth, cur};
}
}
struct Stree {
vec<pii> data;
int size;
Stree() {
size = serialized_tree.size();
data.resize(size << 1);
for (int i = 0; i < size; i++)
data[i + size] = serialized_tree[i];
for (int i = size - 1; i > 0; i--)
data[i] = min(data[i << 1], data[i << 1 | 1]);
}
int query(int a, int b) {
pii ans = {INF, -1};
int l = tree[a].start + size, r = tree[b].start + size;
if (l > r) std::swap(l, r);
for (++r; l < r; l >>= 1, r >>= 1) {
if (l & 1) ans = min(ans, data[l++]);
if (r & 1) ans = min(ans, data[--r]);
}
return ans.second;
}
};
int main() {
std::ios_base::sync_with_stdio(0), std::cin.tie(0);
int n, q;
cin >> n >> q;
tree.resize(n);
serialized_tree.resize((n << 1) - 1);
for (int i = 1; i < n; i++) {
int u;
cin >> u;
u--;
tree[u].neighbors.push_back(i);
}
serialize(0, 0, 0);
Stree LCA;
for (int i = 0; i < q; i++) {
int a, b;
cin >> a >> b;
a--, b--;
cout << LCA.query(a, b) + 1 << "\n";
}
}
Heavy-light Decomposition, HLD
#HLD
#HLD
#重邊
#輕邊
#輕子節點
#重子節點
struct Node {
vec<int> neighbors; // 另一種做法是讓第零個為重子節點
int hson = null; // 重子節點,子樹(含本身)大小
};
vec<Node> tree;
int preprocess(int cur, int pre) { // 回傳子樹大小
int sub_size = 1;
int max_son_size = 0;
for (const int &nxt : tree[cur].neighbors) {
if (nxt == pre) continue;
int nxt_size = preprocess(nxt, cur);
sub_size += nxt_size;
if (nxt_size > max_son_size) {
max_son_size = nxt_size;
tree[cur].hson = nxt;
}
}
return sub_size;
}
A
B
此時 A, B 鍊頂相同說明在同條鍊
所以深度較淺者 B 為 LCA
#HLD LCA
A'
B'
此時 B' 的鍊頂比較深
B' 向上爬
#HLD LCA
A'
B'
此時 A' 的鍊頂比較深
A' 向上爬
#HLD LCA
A'
B'
此時 A' 的鍊頂比較深
A' 向上爬
#HLD LCA
A'
B'
A', B' 鍊頂相同,較淺者 A' 為 LCA
#HLD LCA
A'
B'
A', B' 鍊頂相同,較淺者 A' 為 LCA
#HLD LCA
#include <stdio.h>
#include <functional>
#include <vector>
using std::vector;
const int max_n = 2e5 + 1;
const int INF = 2e9;
struct node {
int parent = 1, size = 1, hson = 0, depth = 0, top = 1, dfn = -1;
int value;
vector<int> neighbor;
} graph[max_n];
struct stree {
inline static int tree[max_n << 1];
int size;
inline void build() {
for (int i = size - 1; i > 0; i--) {
tree[i] = std::max(tree[i << 1], tree[i << 1 | 1]);
}
return;
}
inline int query(int l, int r) {
int ans = -INF;
for (l += size, r += size; l < r; l >>= 1, r >>= 1) {
if (l & 1) ans = std::max(ans, tree[l++]);
if (r & 1) ans = std::max(ans, tree[--r]);
}
return ans;
}
inline void modify(int pos, int x) {
for (pos += size, tree[pos] = x; pos > 1; pos >>= 1) {
tree[pos >> 1] = std::max(tree[pos], tree[pos ^ 1]);
}
return;
}
} max;
void dfs1(int cur) {
static int depth = 0;
graph[cur].depth = ++depth;
int max_size = 0;
for (const auto& next : graph[cur].neighbor) {
if (!graph[next].depth) {
graph[next].parent = cur;
dfs1(next);
graph[cur].size += graph[next].size;
if (graph[next].size > max_size) {
max_size = graph[next].size;
graph[cur].hson = next;
}
}
}
--depth;
return;
}
void dfs2(int cur) {
static int dfn = 0;
graph[cur].dfn = dfn;
max.tree[dfn + max.size] = graph[cur].value, dfn++;
if (graph[cur].hson) {
graph[graph[cur].hson].top = graph[cur].top;
dfs2(graph[cur].hson);
}
else {
return;
}
for (const auto& next : graph[cur].neighbor) {
if (next != graph[cur].parent && next != graph[cur].hson) {
graph[next].top = next;
dfs2(next);
}
}
return;
}
int main() {
int n, q;
scanf("%d%d", &n, &q);
max.size = n;
for (int i = 1; i <= n; i++) scanf("%d", &graph[i].value);
int u, v;
for (int i = 0; i < n - 1; i++) {
scanf("%d%d", &u, &v);
graph[u].neighbor.push_back(v);
graph[v].neighbor.push_back(u);
}
dfs1(1);
dfs2(1);
max.build();
int type, x, ans;
while (q--) {
scanf("%d", &type);
if (type == 1) {
scanf("%d%d", &v, &x);
max.modify(graph[v].dfn, x);
}
else {
ans = -INF;
scanf("%d%d", &u, &v);
// LCA
while (graph[u].top != graph[v].top) {
if (graph[graph[u].top].depth < graph[graph[v].top].depth) std::swap(u, v);
ans = std::max(ans, max.query(graph[graph[u].top].dfn, graph[u].dfn + 1));
u = graph[u].top;
u = graph[u].parent;
}
if (graph[u].dfn > graph[v].dfn) std::swap(u, v);
ans = std::max(ans, max.query(graph[u].dfn, graph[v].dfn + 1));
printf("%d\n", ans);
}
}
return 0;
}
Tree Centroid
#include <stdio.h>
#include <utility>
#include <vector>
using std::pair;
using std::vector;
const int max_n = 2e5 + 1;
struct node {
int size = 1;
vector<int> neighbor;
} tree[max_n];
int tree_size;
void pre_size(int cur, int parent) {
for (const auto &next : tree[cur].neighbor) {
if (next != parent) pre_size(next, cur), tree[cur].size += tree[next].size;
}
return;
}
int get_centroid(int cur, int parent) {
for (const auto &next : tree[cur].neighbor) {
if (next != parent) {
if (tree[next].size > (tree_size >> 1)) return get_centroid(next, cur);
}
}
return cur;
}
int main() {
scanf("%d", &tree_size);
int u, v;
for (int i = 0; i < tree_size - 1; i++) {
scanf("%d%d", &u, &v);
tree[u].neighbor.push_back(v);
tree[v].neighbor.push_back(u);
}
pre_size(1, 1);
printf("%d", get_centroid(1, 1));
return 0;
}