版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/guapi2333/article/details/83413770
一道二分好题。
https://www.luogu.org/problemnew/show/P2323
单调性:花费最少的公路要求的费用越低,满足条件的一级&二级公路就越多。
所以,我们二分花费最少的公路的费用即可。
check函数:看在花费最少的公路的费用的限制下,满足条件的公路是否能凑出一棵生成树。我们用类似于kruskal算法的方法进行这个过程,不过有一个地方需要注意:优先选择一级公路费用<=当前限制的边,这样才更有可能使选出的方案中1级公路的条数>=k。如果先选二级公路,那么很有可能再选一级公路时,该条公路可能会由于其两个端点已在当前生成树的点集中而不被选取,从而导致最终的生成树中包含了<k条一级公路。
Code:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ri register int
using namespace std;
const int MAXN=20020;
int n,m,k,maxn,l,r,mid,ans;
int used[MAXN],had[MAXN],fa[MAXN];
struct node{
int u,v,w1,w2,num;
}s[MAXN];
bool cmp(node a,node b)
{
return a.w1 > b.w1;
}
int find(int x)
{
if(x==fa[x]) return x;
return fa[x]=find(fa[x]);
}
bool check(int minn)
{
int stot=0,cnt=0,fst=1;
memset(used,0,sizeof(used));
for(ri i=1;i<=n;i++) fa[i]=i;
for(ri i=1;i<=m;i++)
if(s[i].w1<=minn) { fst=i; break; }
for(ri i=fst;i<=m;i++)//优先挑一级公路费用合法的路
{
int r1=find(s[i].u),r2=find(s[i].v);
if(r1!=r2)
{
stot++,used[s[i].num]=1,cnt++;
fa[r2]=r1;
}
}
if(cnt<k) return 0;
for(ri i=1;i<=fst-1;i++)//挑完一级公路费用合法的路后再挑二级公路
{
if(s[i].w2>minn) continue;
if(stot==n-1&&cnt>=k)
{
for(int i=1;i<=m;i++) had[i]=used[i];
return 1;
}
int r1=find(s[i].u),r2=find(s[i].v);
if(r1!=r2)
{
stot++,used[s[i].num]=2;
fa[r2]=r1;
}
}
return 0;
}
int main()
{
scanf("%d%d%d",&n,&k,&m);
m-=1;
for(ri i=1;i<=m;i++)
{
scanf("%d%d%d%d",&s[i].u,&s[i].v,&s[i].w1,&s[i].w2);
maxn=max(maxn,s[i].w1);
s[i].num=i;
}
sort(s+1,s+m+1,cmp);
l=0,r=maxn+1;
while(l+1<r)//二分最小费用
{
mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid;
}
for(ri i=l;i<=r;i++)
if(check(i)) { ans=i; break; }
cout<<ans<<'\n';
for(ri i=1;i<=m;i++)
if(had[i]) cout<<i<<" "<<had[i]<<'\n';
return 0;
}