题解 CF566C 【Logistical Questions】
CF566C Logistical Questions
题意
- 一棵
n 个节点的树,点有点权,边有边权。 - 两点间的距离定义为两点间边权和的
\frac 32 次方。 - 求这棵树的带权重心。
-
题解
设
首先可以注意到,对于树上的任意一条路径
设
因此,整棵树有且仅有一个点
如果树退化为一条链,则可以二分
点分治到某个点
如何找到这个子树呢?如果每个子树暴力计算一遍
考虑求导。设
则从
时间复杂度
代码
const int N = 2e5 + 7;
int n, w[N], s[N], rt, v[N], ans1;
vector< pi > e[N];
double sum, sd, d[N], ans2 = 1e20;
void dfs(int x, int f, int S) {
s[x] = 1;
int o = 0;
for (ui i = 0; i < e[x].size(); i++) {
int y = e[x][i].fi;
if (v[y] || y == f) continue;
dfs(y, x, S), s[x] += s[y], o = max(o, s[y]);
}
o = max(o, S - s[x]);
if (o <= (S >> 1)) rt = x;
}
void calc(int x, int f, int o, int z) {
sum += pow(z, 1.5) * w[x], d[o] += pow(z, 0.5) * 1.5 * w[x];
for (ui i = 0; i < e[x].size(); i++) {
int y = e[x][i].fi;
if (y == f) continue;
calc(y, x, o, z + e[x][i].se);
}
}
void dfs(int x, int S) {
dfs(x, 0, S), x = rt, dfs(x, 0, S);
if (v[x]) return;
v[x] = 1, sum = sd = 0;
for (ui i = 0; i < e[x].size(); i++) {
int y = e[x][i].fi;
d[y] = 0, calc(y, x, y, e[x][i].se), sd += d[y];
}
if (sum < ans2) ans1 = x, ans2 = sum;
for (ui i = 0; i < e[x].size(); i++) {
int y = e[x][i].fi;
if (sd - d[y] * 2 >= 0) continue;
dfs(y, s[y]);
break;
}
}
int main() {
rd(n);
for (int i = 1; i <= n; i++) rd(w[i]);
for (int i = 1, x, y, z; i < n; i++) rd(x), rd(y), rd(z), e[x].pb(mp(y, z)), e[y].pb(mp(x, z));
dfs(1, n);
printf("%d %.10f\n", ans1, ans2);
return 0;
}