题目意思不说了。
考虑求半径显然先求直径,使用 \(2n-2\) 次询问直接找出答案。
然后考虑我们对于每一个点,其实他与直径上的距离你是可以计算出来的。
怎么算,通过列方程组的方法。
假设直径两端点为 \(x, y\) ,点 \(i\) 到直径的距离为为 \(d\) ,然后设往这个点到直径上的点对应点 \(y\) 的距离为 \(s\) , 到 \(x\) 的距离为 \(t\)。
那么显然可以得到下列几个式子:
\[s + t = dis(0, y) \] \[t + d = dis(0, i) \] \[d + s = dis(i, y) \]然后自己消元可以求解出 \(d\) 和 \(s\) 。
也就是
\[d = \dfrac{(dis(0,i) + dis(i, y) - dis(0, y))}{2} \] \[s = dis(i, y) - d \]求直径时存下每个点距离端点的距离后这个可以直接解决。
然后我们知道这个直径点 \(C\) 可以在 \(y -> x\) 上,也可以在 \(y -> 0\) 上,但是一定在 \(0 -> s\) 上,因为 \(0\) 往 \(y\) 走距离更大。
于是求出每个点的 \(s\), 与 \(dis(s,t) - s\) 之后取 \(\max\), 最后取 \(\min\) 就能得到 半径 \(R\) 。
然后就做完了第一问,考虑第二问怎么做。
我们容易想到的是这个点 \(C\) 可能不止一个,他有可能是两个。
那么我们对于每一个我们从小往大去试。
注意选点后要更新我们的 \(d\) 值。
然后我们要验证选了这个点之后是否平衡。
我们不平衡的条件就是判断有没有一颗子树的叶子多于 \(\dfrac{n}{2}\) 个。
我们转化一下问题,先考虑怎么判断两个点在不在同一个子树内。
可以通过询问他们的距离来判断,具体的。
如果 \(dis(a,b) < d_a + d_b\) 那么这两个就是同一个子树,否则就不是。
那么我们知道这个方法后,我们可以通过判断两两是否在同一子树的方式来确定是否有一个子树的叶子多于 \(\dfrac{n}{2}\) ,问题就变成了查找这些数字重是否存在绝对众数。
然后就这么做就是了,关于那个求众数的算法挺多的,反正我用的是一个很经典的套路。
当 \(i = 1\) 的时候,\(count = 1, value = d_1\)
\(i\) 变为 \(i + 1\) 时,令 \(count++\) 否则 \(count--\) 。
若 \(count = 0\) 令 \(count = 1, value = d_i\)
反复执行这个过程直到 \(i = n\) 。
然后这个算法其实就是这道题目 「MCOI-03」金牌 。
知道这个之后做题就会容易很多,然后容易发现我们的交互次数是 \(2n\) 的,但是这样就超了。
于是考虑剪枝,当相同的时候跳过就好了。
具体见代码。
#include <bits/stdc++.h>
#include "towns.h"
using namespace std;
const int M = 2e6;
int n, d[M], d1[M], d2[M], d3[M], d4[M], r[M], w[M];
bool check(int x) {
for(int i = 0; i < n; i++) {
if(d3[i] > x) d2[i] += d3[i] - x;
else d2[i] += x - d3[i];
w[i] = 0;
}
int u = 0, num = 0;
for(int i = 0; i < n; i++) {
if(!num) { u = i; num = 1; w[i]++;}
else {
if(getDistance(u, i) < d2[u] + d2[i]) w[u]++, num++;
else w[i]++, num--;
}
}
if(!num) return 1; num = 0;
for(int i = 0; i < n; i++) {
if(!w[i]) continue;
if(getDistance(u, i) < d2[u] + d2[i]) num += w[i];
else num -= w[i];
}
if(num <= 0) return 1;
return 0;
}
int hubDistance(int N, int sub) {
n = N;
int x = 0, y = 0;
for(int i = 0; i < n; i++) d[i] = d2[i] = d3[i] = d4[i] = r[i] = 0;
for(int i = 1; i < n; i++) d[i] = getDistance(0, i);
for(int i = 0; i < n; i++) if(d[i] > d[x]) x = i;
for(int i = 0; i < n; i++) {
d1[i] = getDistance(x, i);
d2[i] = (d[i] + d1[i] - d[x]) >> 1;
d4[i] = d3[i] = d1[i] - d2[i];
}
for(int i = 0; i < n; i++) if(d1[i] > d1[y]) { y = i;}
sort(d4, d4 + n);
for(int i = 0; i < n; i++) {
r[i] = d4[i];
if(d1[y] - r[i] > r[i]) r[i] = d1[y] - r[i];
}
int R = r[0], id = 0;
for(int i = 1; i < n; i++) if(r[i] < R) R = r[i];
if(sub <= 2) return R;
if(r[(n - 1) >> 1] == R) {
if(check(d4[(n - 1) >> 1])) return R;
return -R;
}
if(r[n >> 1] == R) {
if(check(d4[(n - 1) >> 1])) return R;
return -R;
}
return -R;
}