【NOIP2017提高A组模拟10.8】Star Way To Heaven(欧几里得距离最小生成树Prim做法)

Description:

【NOIP2017提高A组模拟10.8】Star Way To Heaven(欧几里得距离最小生成树Prim做法)_第1张图片
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
#include
#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;
    }
}

你可能感兴趣的:(分治,最小生成树)