题目链接
题意
n(n≤200000)个城市形成一棵树,每条边有权值C(i,j)。任意两个点的容量S(i,j)定义为i与j唯一通路上容量的最小值。找一个点(它将成为中心城市),使得它到其他所有点的容量之和最大。
分析
本题直接思路是:找到边权最小的边
(
u
,
v
)
(u,v)
(u,v),记其边权为
w
w
w,将此边拿掉后变成两棵子树,设两子树的结点数分别为
c
[
u
]
c[u]
c[u]、
c
[
v
]
c[v]
c[v],两子树的最大容量之和分别为
d
[
u
]
d[u]
d[u]、
d
[
v
]
d[v]
d[v],根节点要么在子树
u
u
u要么在子树
v
v
v,因此答案为
m
a
x
(
c
[
u
]
∗
w
+
d
[
v
]
,
c
[
v
]
∗
w
+
d
[
u
]
)
max(c[u]*w+d[v],\;c[v]*w+d[u])
max(c[u]∗w+d[v],c[v]∗w+d[u]),递归处理子树即可求出
d
[
u
]
d[u]
d[u]、
d
[
v
]
d[v]
d[v]最终得到答案。
但这时候的递归就不好写了,如果主体思路还是上面这样的话,考虑到结点数很大(n≤200000),不超时的方法应该是遍历排序后的树边顺便就把答案求出来了。
确实能做到遍历排序后的树边顺便就把答案求出来,思路是利用并查集消除递归:把树边按权值从大到小排序,然后依次遍历各条边,根据
m
a
x
(
c
[
u
]
∗
w
+
d
[
v
]
,
c
[
v
]
∗
w
+
d
[
u
]
)
max(c[u]*w+d[v],\;c[v]*w+d[u])
max(c[u]∗w+d[v],c[v]∗w+d[u])将两子树合并。
AC 代码
#include <iostream>
#include <algorithm>
using namespace std;
#define N 200010
int a[N], b[N], c[N], e[N], f[N], n; long long d[N], w[N];
bool cmp(int i, int j) {
return w[i] > w[j];
}
int find(int x) {
return x == f[x] ? x : f[x] = find(f[x]);
}
void solve() {
for (int i=1; i<=n; ++i) {
c[i] = 1; d[i] = 0; f[i] = i;
if (i < n) cin >> a[i] >> b[i] >> w[i], e[i] = i;
}
sort(e+1, e+n, cmp);
long long ans = 0;
for (int i=1; i<n; ++i) {
int u = find(a[e[i]]), v = find(b[e[i]]);
ans = d[v] = max(c[u] * w[e[i]] + d[v], c[v] * w[e[i]] + d[u]); c[v] += c[u]; f[u] = v;
}
cout << ans << endl;
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
while (cin >> n) solve();
return 0;
}