Codeforces 1059D(数学+二分)

Codeforces 1059D
题意:给你平面上一些点,你需要找出一个与直线$y=0$相切的圆使得包含所有点。
彻头彻尾的数学题……
显然如果$y$有正有负就不能构造圆使得与$y=0$相切。
考虑二分半径,然后验证答案。
观察可得,与直线$y=0$相切的圆圆心一定在$y=b$上,也就是圆的纵坐标
我们可以尝试枚举所有点,然后在$y=b$上找到一个范围使得在当前半径下都能覆盖这个点,然后取交集即可。
点$(x,y)$范围相当于解圆不等式$(x-a)^2+(y-r)^2 \leq r^2$,我们解得$a=x±\sqrt{2rt-y^2}$,因为这个不等式是关于$a$的一元二次不等式,二次项系数为正,所以我们取$[x-\sqrt{2rt-y^2},x+\sqrt{2rt-y^2}]$,这个就是范围。

注意二分的范围,也就是最大可能半径的计算:
考虑极端情况$(-10^7, 1), (10^7, 1)$,我们过这两点作圆,将这两点连线后再与圆心形成三角形,根据$(r-1)^2+(10^7)^2=r^2$,可解得$r=\frac{10^{14}+1}{2}$,那么这个就是最大的半径长。

还有就是注意还有可能圆心在$y=-b$上,类似地特判一下即可。如果开方里面是负的说明当前半径无解

知识点:1、浮点数比较
2、二分范围千万不要想当然
3、这种几何题和数学相关,也和二分/三分相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<set>
#include<queue>
#include<climits>
#define ms(i, j) memset(i, j, sizeof i)
#define LL long long
#define db double
const LL MAXN = 100000 + 5, LOGS = 17, INF = 300000000000005ll;
struct edge {LL u, v, d;};
struct data {
LL u, d;
bool operator < (const data &b) const {return d > b.d;}
};
std::vector<edge > G[MAXN];
std::set<std::pair<LL, LL> > nt;
std::priority_queue<data > q;
LL n, m, Q, vis[MAXN], dep[MAXN], pre[MAXN][30], h[MAXN], dis[50][MAXN];
void dfs(LL u, LL fa) {
dep[u] = dep[fa] + 1, vis[u] = 1, pre[u][0] = fa;
for (LL i = 1; i <= LOGS; i++) pre[u][i] = pre[pre[u][i - 1]][i - 1];
for (LL i = 0; i < (LL)G[u].size(); i++) {
edge &e = G[u][i];
if (e.v != fa && !vis[e.v]) {
h[e.v] = h[u] + e.d;
dfs(e.v, u);
if (u < e.v) nt.erase(std::make_pair(u, e.v)); else nt.erase(std::make_pair(e.v, u));
}
}
}
void dij(LL st, LL d[MAXN]) {
for (LL i = 0; i <= n; i++) d[i] = INF, vis[i] = 0;
d[st] = 0, q.push((data){st, 0});
while (!q.empty()) {
data p = q.top(); q.pop();
if (vis[p.u]) continue; vis[p.u] = 1;
for (LL i = 0; i < (LL)G[p.u].size(); i++) {
edge &e = G[p.u][i];
if (d[e.v] > d[p.u] + e.d) d[e.v] = d[p.u] + e.d, q.push((data){e.v, d[e.v]});
}
}
}
LL LCA(LL a, LL b) {
if (dep[a] < dep[b]) std::swap(a, b);
for (LL i = LOGS; i >= 0; i--) if (dep[pre[a][i]] >= dep[b]) a = pre[a][i];
if (a == b) return a;
for (LL i = LOGS; i >= 0; i--) if (pre[a][i] != pre[b][i]) a = pre[a][i], b = pre[b][i];
return pre[a][0];
}
LL getdist(LL u, LL v) {return h[u] + h[v] - 2 * h[LCA(u, v)];}
void clean() {
ms(h, 0), ms(vis, 0), ms(dep, 0);
}
int solve() {
clean();
for (LL u, v, d, i = 1; i <= m; i++) {
scanf("%lld%lld%lld", &u, &v, &d);
G[u].push_back((edge){u, v, d}), G[v].push_back((edge){v, u, d});
if (u < v) nt.insert(std::make_pair(u, v)); else nt.insert(std::make_pair(v, u));
}
dfs(1, 0);
//for (LL i = 0; i < (LL)nt.size(); i++) printf("u=%lld v=%lld d=%lld\n", nt[i].u, nt[i].v, nt[i].d);
LL cnt = 0;
for (std::set<std::pair<LL, LL> >::iterator it = nt.begin(); it != nt.end(); it++) {
std::pair<LL, LL> e = *it;
LL &u = e.first, &v = e.second;
//printf("u=%lld v=%lld d=%lld\n", nt[i].u, nt[i].v, nt[i].d);
if (u > v) continue;
dij(u, dis[++cnt]);
}
//for (LL i = 1; i <= n; i++) for (LL j = 1; j <= n; j++) printf("i=%lld j=%lld lca=%lld\n", i, j, LCA(i, j));
scanf("%lld", &Q);
while (Q--) {
LL u, v; scanf("%lld%lld", &u, &v);
LL ans = getdist(u, v);
for (LL i = 1; i <= cnt; i++) {
ans = std::min(ans, dis[i][u] + dis[i][v]);
}
printf("%lld\n", ans);
}
return 0;
}
int main() {
scanf("%lld%lld", &n, &m), solve();
return 0;
}

------ 本文结束 ------