Hướng giải của Tìm đỉnh gần nhất
Nhớ rằng hướng dẫn giải này chỉ nên sử dụng khi bế tắc, và tuyệt đối không nên sao chép mã nguồn kèm theo. Hãy tôn trọng tác giả bài tập và người viết hướng dẫn giải.
Nộp mã nguồn lời giải chính thức trước khi giải bài tập đó có thể khiến bạn bị ban.
Nộp mã nguồn lời giải chính thức trước khi giải bài tập đó có thể khiến bạn bị ban.
Tác giả:
Lời giải
Ý tưởng
Bài toán QTREE5 kinh điển. Sử dụng Centroid Decomposition:
- Với mỗi centroid, lưu một
multisetchứa khoảng cách từ nó đến tất cả các đỉnh được đánh dấu trong subtree của nó. - Khi bật/tắt một đỉnh \(u\), duyệt từ \(u\) lên centroid tree, tại mỗi ancestor \(c\), cập nhật
multiset[c]với \(dist(u,c)\). - Khi truy vấn \(u\), duyệt từ \(u\) lên centroid tree, lấy \(dist(u,c) + min(multiset[c])\).
- Cần LCA để tính khoảng cách \(O(\log N)\).
Độ phức tạp
- Khởi tạo: \(O(N \log N)\).
- Mỗi truy vấn: \(O(\log^2 N)\).
Code mẫu
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005, LOG = 17;
vector<int> adj[MAXN];
int subsize[MAXN], depth[MAXN], up[MAXN][LOG];
int centroid_parent[MAXN];
bool removed[MAXN], colored[MAXN];
multiset<int> color_dist[MAXN];
int n;
void dfs_lca(int u, int p) {
// Chuẩn bị LCA
up[u][0] = p;
for (int i = 1; i < LOG; i++) {
if (up[u][i-1] != -1)
up[u][i] = up[up[u][i-1]][i-1];
else
up[u][i] = -1;
}
for (int v : adj[u]) {
if (v != p) {
depth[v] = depth[u] + 1;
dfs_lca(v, u);
}
}
}
int lca(int u, int v) {
// Tìm LCA
if (depth[u] < depth[v]) swap(u, v);
for (int i = LOG-1; i >= 0; i--)
if (up[u][i] != -1 && depth[up[u][i]] >= depth[v])
u = up[u][i];
if (u == v) return u;
for (int i = LOG-1; i >= 0; i--)
if (up[u][i] != up[v][i])
u = up[u][i], v = up[v][i];
return up[u][0];
}
int dist(int u, int v) {
// Tính khoảng cách
int l = lca(u, v);
return depth[u] + depth[v] - 2 * depth[l];
}
void dfs_size(int u, int p) {
subsize[u] = 1;
for (int v : adj[u])
if (v != p && !removed[v]) {
dfs_size(v, u);
subsize[u] += subsize[v];
}
}
int find_centroid(int u, int p, int tot) {
for (int v : adj[u])
if (v != p && !removed[v] && subsize[v] > tot / 2)
return find_centroid(v, u, tot);
return u;
}
void decompose(int u, int pc) {
// Xây centroid tree
dfs_size(u, -1);
int c = find_centroid(u, -1, subsize[u]);
centroid_parent[c] = pc;
removed[c] = true;
for (int v : adj[c])
if (!removed[v]) decompose(v, c);
}
void toggle(int u) {
// Bật/tắt đỉnh u, cập nhật tất cả ancestor trên centroid tree
int cur = u;
while (cur != -1) {
int d = dist(u, cur);
if (colored[u]) {
auto it = color_dist[cur].find(d);
if (it != color_dist[cur].end())
color_dist[cur].erase(it);
} else {
color_dist[cur].insert(d);
}
cur = centroid_parent[cur];
}
colored[u] = !colored[u];
}
int query(int u) {
// Tìm đỉnh được đánh dấu gần u nhất
int ans = INT_MAX;
int cur = u;
while (cur != -1) {
if (!color_dist[cur].empty()) {
ans = min(ans, dist(u, cur) + *color_dist[cur].begin());
}
cur = centroid_parent[cur];
}
return ans == INT_MAX ? -1 : ans;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cin >> n;
for (int i = 0; i < n-1; i++) {
int u, v; cin >> u >> v;
adj[u].push_back(v); adj[v].push_back(u);
}
depth[1] = 0;
memset(up, -1, sizeof(up));
dfs_lca(1, -1);
decompose(1, -1);
int q; cin >> q;
while (q--) {
int t, u; cin >> t >> u;
if (t == 1) toggle(u);
else cout << query(u) << "\n";
}
return 0;
}
Nhận xét