福建WC2014 路径权值(Kruskal重构树 + 树状数组)

题目描述:

给定一个带权树,树上任意两点间的路径权值 d(x,y) 定义为 x,y 这两个点之间路径上的最小值,树上任意一点 x 的权值定义为这个点到树上其他所有点的路径权值和,即 d(x,i),现求树上每一个点的路径权值和。

Input Format

首先输入一个整数 n(1n105) ,表示树的点的个数。 
接下来 n1 行,每行三个整数 x,y,s(1x,yn,1s1000) ,表示编号为 x 的节点和编号为 y 的节点之间存在一条权值为 s 的边,树上每个点的编号为 1n

Output Format

n行,每行输出每个节点对应的路径权值和

Sample Input

4 1 2 2 2 4 1 2 3 1

Sample Output

4 4 3 3

思路:

首先明确要求的是一个 xy 的所有路径的最小权值,而树链剖分是求两点路径上的最小 边权或点权行不通。要知道路径的最小值,还可以用 Kruskal重构树 来解决,根据重构树的重要性质可以知道,树上任意两点的最短路径的权值就是这两点在重构树上的 lca ,那么就可以在 O(logn) 的时间复杂度下快速求出路径权值。

/*读入部分*/ 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(n2logn) 的复杂度很明显是会超时的。就要考虑如果优化掉每一次枚举的 O(n2) 的瓶颈。由于原来最大生成树的所有点在重构树中都是叶子节点,其余所有的节点都是代表着原来生成树中的边权。根据这一点可以知道,任何一个节点要到另一个节点一定是从左(右)子树经过该子树的根节点走向右(左)子树的,所以每一个非叶子节点都会对以它为根的子树中所有的节点产生 x 的贡献,也就是一个区间加的操作,可以用树状数组或线段树来实现,从而将计算每一个点到其他所有点的路径之和从暴力枚举的 O(n2) 优化成了 O(nlogn)

/*区间加法*/ 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";

在计算贡献的时候还需要知道左右子树的大小,所以还需要遍历一遍重构树来计算出每一棵子树的大小,左子树中所有的叶子节点都可以到右子树中,所以右子树对左子树的贡献就是 valx×size[] 左子树同理。总的时间复杂度为 O(nlogn) ,省去了每一次要求两点 LCAO(logn)

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; }

__EOF__

本文作者HoneyGrey
本文链接https://www.cnblogs.com/Haven-/p/16855777.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   浅渊  阅读(206)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示