Description:
k<=6000,n,m<=10^6
题解:
直接想找到这样一条路径是很难的。
可以考虑二分答案r,对k个star都建一个半径为r的圆,那么对于两个相交的圆把它们弄到一个集合,对上下边界特殊判断,如果说上下边界被弄到了一个集合,即说明,有若干个圆将矩形拦腰折断,分成了两个不连通的部分,则这个答案不合法。
其实也可以对n个点进行欧几里得距离最小生成树,当然,边界特盼,什么时候上下边界都在生成树里了,则当前生成树的最长边的一半就是答案。
这样的图有n个点,n^2条边,可以直接用prim算法进行生成。
prim算法流程:
有两个点集V,V’,V’一开始为空,V为我要最小生成树的点集。
首先随便从V里移一个点到V’,这题肯定是放一个边界点。
找到一条边,它的两个点分别在V’,V里,并且它的长度最短,把在V里的端点移到V’去,继续这样直到结束。
可以记dis[i]为V’中的点到i的最短距离,每次移了一个点去V’,把这个点相邻的点的dis全部维护一遍,然后找最小的dis就行了。
如果找dis用普通堆维护的话,复杂度就是O(E log |V|),据说斐波拉契堆可以达到O(E +|V| log |V|),这个不会。
但是这题显然不用什么堆,因为已经有N^2条边了, 用堆干什么?直接扫一遍求最小值就是O(N^2)的了。
czy大佬当时说,他考场打了4000bytes+的O(n log n)欧几里得距离最小生成树算法,%%%
Code:
#include<cmath>
#include<cstdio>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
using namespace std;
const int N = 6005;
int n, m, k, x[N], y[N];
double dis[N], ans; int bz[N];
double ds(int x, int y, int l, int r) {
return sqrt((double)(x - l) * (x - l) + (double)(y - r) * (y - r));
}
int main() {
freopen("starway.in", "r", stdin);
freopen("starway.out", "w", stdout);
scanf("%d %d %d", &n, &m, &k);
fo(i, 1, k) scanf("%d %d", &x[i], &y[i]);
fo(i, 1, k) dis[i] = m - y[i]; dis[k + 1] = m;
dis[0] = 2e9;
while(1) {
int mi = 0;
fo(i, 1, k + 1) if(!bz[i] && dis[i] < dis[mi])
mi = i;
ans = max(ans, dis[mi]);
if(mi == k + 1) {
printf("%.10lf", ans / 2);
return 0;
}
fo(i, 1, k) dis[i] = min(dis[i], ds(x[i], y[i], x[mi], y[mi]));
dis[k + 1] = min(dis[k + 1], y[mi]);
bz[mi] = 1;
}
}

本文介绍了如何使用Prim算法解决NOIP2017提高组模拟赛中的Star Way To Heaven问题。通过二分答案和构建半径为r的圆,判断是否存在合法路径。文章详细阐述了Prim算法的流程,并指出在处理此类问题时,由于边数过多,直接扫描求解的效率更高。
&spm=1001.2101.3001.5002&articleId=78345850&d=1&t=3&u=505f6d35114b41f1a7bf2a24b4827864)
1876

被折叠的 条评论
为什么被折叠?



