题目大意:给出三个数n,m,k,代表有n个点m条边,边分为两种一种A类一种B类,建一棵最小生成树其中A类的边要刚好用到k条,问最小花费是多少。
思路:最小生成树+二分,要使得A类边正好用到k条,可以假设如果刚开始是一条也取不到,那么原因是A类的边权值很大,随着权值的减少建最小生成树时取到A类的边会越来越多,那么我们可以用二分来逐渐修改A类边的权值,使得最小生成树A类的边逼近k条,这时二分的左端点就需要是负数了。还有对结构体排序时要先权值优先,权值相等的边要优先选取A类的。
#include<cstdio>
#include<algorithm>
#include<cstring>
#define inf 0x3fffffff
using namespace std;
const int maxn=50050;
int pre[maxn];
struct node
{
int u,v,w,tmp,id;
}edge[maxn*2],edge2[maxn*2];
void creat()
{
for(int i=0;i<maxn;i++)
pre[i]=i;
}
int findroot(int root)
{
if(root==pre[root])
return root;
return pre[root]=findroot(pre[root]);
}
bool cmp(node a,node b)
{
if(a.w==b.w)
return a.tmp<b.tmp;
return a.w<b.w;
}
bool cmp1(node a,node b)
{
return a.id<b.id;
}
int main()
{
int n,m,k;
int u,v,w,tmp,cas=1;
while(scanf("%d %d %d",&n,&m,&k)!=EOF)
{
creat();
for(int i=0;i<m;i++)
{
scanf("%d %d %d %d",&edge[i].u,&edge[i].v,&edge[i].w,&edge[i].tmp);
edge[i].id=edge2[i].id=i;
edge2[i].u=edge[i].u,edge2[i].v=edge[i].v,edge2[i].w=edge[i].w,edge2[i].tmp=edge[i].tmp;
}
int sum=0,num=0,ans,res;
int l=-50001,r=50001;
while(l<=r)
{
int mid=(l+r)/2;
for(int i=0;i<m;i++)
{
if(edge2[i].tmp==0)
{
edge[i].w=edge2[i].w+mid;
}
}
sort(edge,edge+m,cmp);
sum=0,num=0;
creat();
for(int i=0;i<m;i++)
{
int x=findroot(edge[i].u);
int y=findroot(edge[i].v);
if(x!=y)
{
pre[x]=y;
sum+=edge[i].w;
if(edge[i].tmp==0)
num++;
}
}
if(num<k)
{
r=mid-1;
}
else
{
res=sum;
ans=mid;
l=mid+1;
}
sort(edge,edge+m,cmp1);
}
printf("Case %d: %d\n",cas++,res-k*ans); //如果是正的要减回去,负的要加回来
}
return 0;
}