福建WC2014 路径权值(Kruskal重构树 + 树状数组)
题目描述:
给定一个带权树,树上任意两点间的路径权值 \(d\left(x,y\right)\) 定义为 \(x,y\) 这两个点之间路径上的最小值,树上任意一点 \(x\) 的权值定义为这个点到树上其他所有点的路径权值和,即 \(\sum d\left(x,i\right)\),现求树上每一个点的路径权值和。
Input Format
首先输入一个整数 \(n\left(1 \leq n \leq 10^5 \right)\) ,表示树的点的个数。
接下来 \(n−1\) 行,每行三个整数 \(x,y,s\left(1\leq x,y\leq n,1\leq s \leq 1000\right)\) ,表示编号为 \(x\) 的节点和编号为 \(y\) 的节点之间存在一条权值为 \(s\) 的边,树上每个点的编号为 \(1\sim n\)
Output Format
n行,每行输出每个节点对应的路径权值和
Sample Input
4
1 2 2
2 4 1
2 3 1
Sample Output
4
4
3
3
思路:
首先明确要求的是一个 \(x \rightarrow y\) 的所有路径的最小权值,而树链剖分是求两点路径上的最小 边权或点权行不通。要知道路径的最小值,还可以用 Kruskal重构树 来解决,根据重构树的重要性质可以知道,树上任意两点的最短路径的权值就是这两点在重构树上的 \(lca\) ,那么就可以在 \(O\left(\log{n}\right)\) 的时间复杂度下快速求出路径权值。
/*读入部分*/
int n; std::cin >> n;
int now = n;
std::vector<std::array<int, 3>> edge;
std::vector<std::vector<int>> adj(n * 2);
std::vector<int> val(n * 2);
for (int i = 1; i < n; i++) {
int u, v, w;
std::cin >> u >> v >> w;
edge.push_back({w, u, v});
}
/*Ex_kruskal*/
DSU uf(n * 2 + 1);
std::sort(edge.begin(), edge.end(), std::greater<std::array<int, 3>>());
for (auto& [w, u, v] : edge) {
int fu = uf.leader(u), fv = uf.leader(v);
val[++now] = w;
uf.f[fu] = uf.f[fv] = now;
adj[now].push_back(fu), adj[now].push_back(fv);
}
如果是一个点一个点的去计算过去,就会是 \(O\left(n ^ 2 \log{n} \right)\) 的复杂度很明显是会超时的。就要考虑如果优化掉每一次枚举的 \(O\left(n ^ 2\right)\) 的瓶颈。由于原来最大生成树的所有点在重构树中都是叶子节点,其余所有的节点都是代表着原来生成树中的边权。根据这一点可以知道,任何一个节点要到另一个节点一定是从左(右)子树经过该子树的根节点走向右(左)子树的,所以每一个非叶子节点都会对以它为根的子树中所有的节点产生 \(x\) 的贡献,也就是一个区间加的操作,可以用树状数组或线段树来实现,从而将计算每一个点到其他所有点的路径之和从暴力枚举的 \(O\left(n ^ 2\right)\) 优化成了 \(O\left(n\log{n}\right)\) 。
/*区间加法*/
auto change = [&](int x, int v) -> void {
bit.add(seg[x][0], v);
bit.add(seg[x][1] + 1, -v);
};
for (int i = n + 1; i <= now; i++) {
change(adj[i][0], siz[adj[i][1]] * val[i]);
change(adj[i][1], siz[adj[i][0]] * val[i]);
}
for (int i = 1; i <= n; i++) std::cout << bit.sum(seg[i][0]) << "\n";
在计算贡献的时候还需要知道左右子树的大小,所以还需要遍历一遍重构树来计算出每一棵子树的大小,左子树中所有的叶子节点都可以到右子树中,所以右子树对左子树的贡献就是 \(val_x × size\left[右子树\right]\) 左子树同理。总的时间复杂度为 \(O\left(n \log{n}\right)\) ,省去了每一次要求两点 \(LCA\) 的 \(O\left(\log{n}\right)\)。
std::vector<int> siz(now + 1);
std::vector<std::array<int, 2>> seg(now + 1);
int idx = 0;
std::function<void(int)> dfs = [&](int u) -> void {
siz[u] = (u <= n);
seg[u][0] = ++idx; //记录这棵子树的dfs序的范围
for (auto& v : adj[u]) {
dfs(v);
siz[u] += siz[v];
}
seg[u][1] = idx;
};
dfs(now);
完整代码
#include <bits/stdc++.h>
using i64 = long long;
#define rep(i,a,b) for (int i = a; i < b; i++)
#define per(i,a,b) for (int i = a; i >= b; i--)
#define SZ(s) int(s.size())
#define all(v) v.begin(), v.end()
template <typename T>
struct Fenwick {
const int n;
std::vector<T> tr;
Fenwick(int n) : n(n), tr(n + 1) {}
void add(int x, T v) {
for (int i = x; i <= n; i += i & -i)
tr[i] += v;
}
T sum(int x) {
T ans = 0;
for (int i = x; i; i-=i&-i) ans += tr[i];
return ans;
}
T rangeSum(int l, int r) {return sum(r) - sum(l);}
int query(T s) { // 查询1~pos的和小于等于s
int pos = 0;
for (int j = 18; j >= 0; j -- )
if ((pos + (1ll << j) < n) && tr[pos + (1ll << j)] <= s)
pos = (pos + (1 << j)), s -= tr[pos];
return pos;
}
};
struct DSU {
std::vector<int> f, siz;
DSU(int n) : f(n), siz(n, 1) { std::iota(f.begin(), f.end(), 0); }
int leader(int x) {
while (x != f[x]) x = f[x] = f[f[x]];
return x;
}
bool same(int x, int y) { return leader(x) == leader(y); }
bool merge(int x, int y) {
x = leader(x);
y = leader(y);
if (x == y) return false;
siz[x] += siz[y];
f[y] = x;
return true;
}
int size(int x) { return siz[leader(x)]; }
};
signed main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
int n; std::cin >> n;
int now = n;
std::vector<std::array<int, 3>> edge;
std::vector<std::vector<int>> adj(n * 2);
std::vector<int> val(n * 2);
for (int i = 1; i < n; i++) {
int u, v, w;
std::cin >> u >> v >> w;
edge.push_back({w, u, v});
}
DSU uf(n * 2 + 1);
std::sort(edge.begin(), edge.end(), std::greater<std::array<int, 3>>());
for (auto& [w, u, v] : edge) {
int fu = uf.leader(u), fv = uf.leader(v);
val[++now] = w;
uf.f[fu] = uf.f[fv] = now;
adj[now].push_back(fu), adj[now].push_back(fv);
}
Fenwick<int> bit(now);
std::vector<int> siz(now + 1);
std::vector<std::array<int, 2>> seg(now + 1);
int idx = 0;
std::function<void(int)> dfs = [&](int u) -> void {
siz[u] = (u <= n);
seg[u][0] = ++idx;
for (auto& v : adj[u]) {
dfs(v);
siz[u] += siz[v];
}
seg[u][1] = idx;
};
dfs(now);
auto change = [&](int x, int v) -> void {
bit.add(seg[x][0], v);
bit.add(seg[x][1] + 1, -v);
};
for (int i = n + 1; i <= now; i++) {
change(adj[i][0], siz[adj[i][1]] * val[i]);
change(adj[i][1], siz[adj[i][0]] * val[i]);
}
for (int i = 1; i <= n; i++) std::cout << bit.sum(seg[i][0]) << "\n";
return 0 ^ 0;
}

浙公网安备 33010602011771号