ABC 214D Sum of Maximum Weights(并查集模拟删边)
ABC 214D Sum of Maximum Weights(并查集模拟删边)
Sum of Maximum Weights
给出有 \(n\;(2 \le n \le 1e5)\)个点的一棵树,定义\(f(x, y)\)表示从节点 x 到节点 y 的最短路中的最大边权。
请输出\(\sum_{i=1}^{N-1} \sum_{j=i+1}^{N}\,f(i, j)\)
思路:
首先我们可以知道,由于这是一棵树,所以路径是唯一的。那么从 i 到 j 的路径上,只有一个最大的边权会造成贡献,我们就考虑往贡献的方向思考。对于一条边来说,当且仅当这条边为某两个点路径中最大边权的时候,会对答案造成贡献。
我们把通过这条边的所有点分成两个集合,可以发现以这条边为最大边权的那些点具有一个特点:就是在连接该条边使他们联通之前,没有比这个边权值更大的边,这个时候,这条边就会对那些点做出贡献。所以我们就可以从小到大连边,模拟一个删边的过程,也是类似一个最小生成树的过程。
代码
边权从小到大排序,用这条边连接的集合大小乘上边权就是这条边对答案的贡献。
const int N = 100005;
vector<array<int, 3> > vc;
int fa[N];
ll cnt[N];
int n;
int fd(int x)
{
if(fa[x] != x) fa[x] = fd(fa[x]);
return fa[x];
}
int main()
{
cin >> n;
for(int i = 1; i < n; i ++)
{
int u, v, w;
cin >> u >> v >> w;
vc.push_back({w, u, v});
}
for(int i = 1; i <= n; i ++)
fa[i] = i, cnt[i] = 1;
sort(all(vc));
ll res = 0;
for(auto x : vc)
{
int w = x[0], u = x[1], v = x[2];
int fx = fd(u), fy = fd(v);
if(fx == fy) continue;
res += cnt[fx] * cnt[fy] * w;
cnt[fy] += cnt[fx];
fa[fx] = fy;
}
cout << res << '\n';
}