题意:
给定n个点,m条边,两个点之间用线连接起来。要么是联通的线,要么是移动的线,问必须要选k条电信线的最小生成树是多少
解析:
再求生成树的时候会遇到两种情况。 cnt为选择电信线的个数
第一种 k>cnt 说明我们在选择边的时候,电信线的权值都很大,所以选的很少
第二种 k<cnt 说明我们在选择边的时候,电信线权值很小,我们都会优先选择
处理的方法就是对于第一种我们可以给电信线权值都减小,使其得到满足
对于第二种我们可以给电信线权值都增加,使其得到满足
所以我们二分出一个增量mid ,然后不断的判断电信线的个数是否满足k个。
答案就是最小生成树和-k*mid
#pragma GCC optimize(3 , "Ofast" , "inline")
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int fa[N];
int n,m,k,p,sum,cnt;
struct node
{
int u,v,w,id;
}a[N<<1];
int find(int x)
{
if(fa[x]!=x) return fa[x]=find(fa[x]);
return fa[x];
}
void init()
{
for(int i=0;i<N;i++) fa[i]=i;
}
bool cmp(const node &a,const node &b)
{
if(a.w==b.w) return a.id<b.id;
return a.w<b.w;
}
bool check(int x)
{
sum=0;cnt=0;
for(int i=1;i<=m;i++) if(a[i].id==0) a[i].w+=x;
sort(a+1,a+1+m,cmp);
for(int i=1;i<=m;i++)
{
int x=a[i].u;
int y=a[i].v;
int u=find(x),v=find(y);
if(u!=v){
sum+=a[i].w;
fa[u]=v;
if(a[i].id==0) cnt++;
}
}
for(int i=1;i<=m;i++) if(a[i].id==0) a[i].w-=x;
if(cnt>=k) return true;
return false;
}
int main()
{
p=0;
while(~scanf("%d %d %d",&n,&m,&k))
{
int ans=0;
for(int i=1;i<=m;i++) scanf("%d %d %d %d",&a[i].u,&a[i].v,&a[i].w,&a[i].id);
int l=-100,r=100;
while(l<=r)
{
init();
int mid=l+r>>1;
if(check(mid))
{
l=mid+1;
ans=sum-k*mid;
}
else r=mid-1;
}
printf("Case %d: %d\n",++p,ans);
}
}