[luogu1991]无线通讯网

传送门

Solution

考虑用二分答案去找最小的D,然后跑一跑Kruskal,大于D的边忽略掉,跑完之后查看有几个联通块,数量小于S即可。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define MAXN 505
#define eps 0.0001

int fa[MAXN];
struct Node{
    int x,y;
}V[MAXN];
struct edge{
    int u,v;
    double w;
}E[MAXN*MAXN];
int vis[MAXN];

int S,P;
int tot = 0;

inline double dis(Node a,Node b){
    return (double)sqrt((double)(a.x-b.x)*(a.x-b.x)+(double)(a.y-b.y)*(a.y-b.y));
}

inline void add(int u,int v){
    E[++tot].u = u;E[tot].v = v;E[tot].w = dis(V[u],V[v]);
}

inline bool cmp(edge a,edge b){
    return a.w < b.w;
}

int find(int u){return u==fa[u]?u:fa[u]=find(fa[u]);}
inline void merge(int u,int v){
    int xx = find(u);int yy = find(v);
    if(xx==yy)return;
    fa[xx]=yy;
}

inline void Kruskal(double size){

    for(register int i=1;i<=P;++i)fa[i] = i;

    int count = 0;
    for(register int i=1;i<=tot;++i){
        if(E[i].w<=size||fabs(E[i].w-size)<=eps){
            if(find(E[i].u)!=find(E[i].v)){
                merge(E[i].u,E[i].v);
                count ++;
            }
        }
        if(count == P-1)break;
    }
    for(register int i=1;i<=P;++i)fa[i] = find(i);
}
inline bool Check(double size){

    Kruskal(size);
    
    int count = 0;
    for(register int i=1;i<=P;++i)vis[i] = 0;
    for(register int i=1;i<=P;++i){
        if(!vis[fa[i]]){
            vis[fa[i]] = 1;
            count++;
        }
    }

    return count <= S;
}

int main(){

    scanf("%d%d",&S,&P);
    for(register int i=1;i<=P;++i){
        scanf("%d%d",&V[i].x,&V[i].y);
        for(register int j=1;j<i;++j){
            add(i,j);
        }
    }

    std::sort(E+1,E+1+tot,cmp);
    double l = 0;
    double r = 10000.000000001;

    while(fabs(l-r)>eps){
        double mid = (l+r)/2.00000000001;
        if(Check(mid))r = mid;
        else l = mid;
    }

    printf("%.2lf",l);
    return 0;
}

但是这道题不需要二分。做完看的题解

跑一遍Kruskal,记录当前最大值,并且查看联通块数量,只要数量小等于S就可以停止了,当前最大值为答案。

猜你喜欢

转载自www.cnblogs.com/Neworld2002/p/9629893.html