问题 I: Two Famous Companies-----------思维(二分+MST)

28 篇文章 0 订阅
9 篇文章 0 订阅

在这里插入图片描述
在这里插入图片描述
题意:
给定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);
  }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值