公路修建问题题解

公路修建问题题解

一看到题目中“花费最大的公路最小”,

我们就想到了二分,

于是乎,我们就有了最大的边x,

还需要满足的条件是:

1.有k条花费小于x的一级公路,且这k条不构成环(因为是要树中有k条一级公路)

2.所有花费小于x的公路能构成一条最小生成树

用并查集维护连通,用Kruskal的思想能加就尽量加。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=20006;
int n,k,m,p,t,f[N];
struct xd{int x,y,c1,c2;}a[N];
inline int read(){
   int T=0,F=1; char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();}
   while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar();
   return F*T;
}
int getf(int u){return f[u]==u?u:f[u]=getf(f[u]);}
bool merge(int u,int v){
     u=getf(u),v=getf(v);
     if(u==v) return 0;
     f[u]=v; return 1;
}
bool check(int u){
     p=0;
     for(int i=1;i<=n;++i) f[i]=i;
     for(int i=1;i<=m;++i) if(a[i].c1<=u) if(merge(a[i].x,a[i].y)) ++p; 
     if(p<k) return 0;
     for(int i=1;i<=m;++i) if(a[i].c2<=u) if(merge(a[i].x,a[i].y)) ++p; 
     if(p<n-1) return 0;
     return 1;
}
int ef(int l,int r){
    int mid=(l+r>>1);
    if(l==r) return l;
    if(check(mid)) return ef(l,mid);
    else return ef(mid+1,r);
}
int main(){
    n=read(),k=read(),m=read()-1;
    for(int i=1;i<=m;++i) a[i].x=read(),a[i].y=read(),a[i].c1=read(),a[i].c2=read();
    t=ef(1,30000),printf("%d\n",t);
    for(int i=1;i<=n;++i) f[i]=i;
    for(int i=1;i<=m;++i) if(a[i].c1<=t) if(merge(a[i].x,a[i].y)) printf("%d %d\n",i,1);
    for(int i=1;i<=m;++i) if(a[i].c2<=t) if(merge(a[i].x,a[i].y)) printf("%d %d\n",i,2);
    return 0;
} 
posted @ 2019-08-08 08:45  lsoi_ljk123  阅读(192)  评论(0编辑  收藏  举报