D
题目描述
问题陈述
我们有一棵树,它有
N
N
N 个顶点,编号为
1
,
2
,
…
,
N
1, 2, \dots, N
1,2,…,N。
连接顶点
u
i
u_i
ui 和顶点
v
i
v_i
vi 的
i
i
i-th 边
(
1
≤
i
≤
N
−
1
)
(1 \leq i \leq N - 1)
(1≤i≤N−1) 的权重为
w
i
w_i
wi。
对于不同的顶点 u u u和 v v v,让 f ( u , v ) f(u, v) f(u,v)成为从顶点 u u u到顶点 v v v的最短路径中所包含的边的最大权重。
查找 ∑ i = 1 N − 1 ∑ j = i + 1 N f ( i , j ) \displaystyle \sum_{i = 1}^{N - 1} \sum_{j = i + 1}^N f(i, j) i=1∑N−1j=i+1∑Nf(i,j)。
思路分析
每两个点的最短路中对答案有贡献的是权值最大的边,设这个边的权值为x,所以当权值<x的边加入时,这两个点一定没有联通。
由此可以考虑并查集算法,我们可以将所有边按照权重从小到大排序,依次加入树中,若加入的这条边连接起来了两个连通块,那个对于这两个连通块之间的所有路径来说,首先他们一定是最短路径(因为是第一次被连起来),其次当前边也是权值最大的边,所以对答案的贡献为sz[pa] * 1ll * sz[pb] * c
代码
int n;
int p[N], sz[N];
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
struct edge{
int a, b, c;
bool operator < (const edge& w) const
{
return c < w.c;
}
};
void solve()
{
cin >> n;
vector<edge> edges;
for (int i = 1; i < n; i++)
{
int a, b, c;
cin >> a >> b >> c;
edges.push_back({a, b, c});
}
sort(edges.begin(), edges.end());
for (int i = 1; i <= n; i++) p[i] = i, sz[i] = 1;
LL res = 0;
for (auto &[a, b, c] : edges)
{
int pa = find(a), pb = find(b);
if (pa == pb) continue;
p[pb] = pa;
res += sz[pa] * 1ll * sz[pb] * c;
sz[pa] += sz[pb];
}
cout << res << endl;
}
```