Hướng giải của Tổng khoảng cách
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 tổng khoảng cách có thể giải bằng centroid decomposition hoặc DFS 2 lần đơn giản hơn.
Cách 1 (DFS 2 lần - Khuyến nghị):
- DFS lần 1: Tính kích thước subtree và tổng khoảng cách từ gốc đến mọi đỉnh.
- DFS lần 2: Khi đi từ cha \(p\) xuống con \(v\): \(ans[v] = ans[p] - sz[v] + (N - sz[v])\).
Cách 2 (Centroid Decomposition):
- Tại mỗi centroid, tính tổng khoảng cách đến các đỉnh trong từng subtree.
- Duy trì thông tin tổng khoảng cách và số lượng đỉnh để tính đóng góp.
Độ phức tạp
- Thời gian: \(O(N)\) (DFS 2 lần) hoặc \(O(N \log N)\) (Centroid Decomposition).
- Bộ nhớ: \(O(N)\).
Code mẫu (Centroid Decomposition)
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200005;
vector<int> adj[MAXN];
int subsize[MAXN], cnt[MAXN];
long long sum_dist[MAXN], subtree_sum[MAXN];
bool removed[MAXN];
int n;
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 collect(int u, int p, long long d, vector<pair<long long,int>>& out, int comp_id) {
// Thu thập (khoảng cách, đỉnh) trong một subtree
out.push_back({d, u});
sum_dist[u] += d;
cnt[u]++;
for (int v : adj[u])
if (v != p && !removed[v])
collect(v, u, d + 1, out, comp_id);
}
void decompose(int u) {
dfs_size(u, -1);
int c = find_centroid(u, -1, subsize[u]);
removed[c] = true;
long long total_dist = 0;
int total_cnt = 0;
for (int v : adj[c]) {
if (removed[v]) continue;
vector<pair<long long,int>> nodes;
collect(v, c, 1, nodes, v);
for (auto [d, node] : nodes) {
// Đóng góp từ các subtree khác
sum_dist[node] += total_dist + (long long)total_cnt * d;
cnt[node] += total_cnt;
}
for (auto [d, node] : nodes) {
total_dist += d;
total_cnt++;
}
}
for (int v : adj[c])
if (!removed[v]) decompose(v);
}
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);
}
// Khởi tạo sum_dist[i] = 0, cnt[i] = 1 (tự thân)
for (int i = 1; i <= n; i++) cnt[i] = 1;
decompose(1);
for (int i = 1; i <= n; i++)
cout << sum_dist[i] << "\n";
return 0;
}
Nhận xét