[bzoj 2654] tree

12 篇文章 0 订阅

跪神题,跪秒题大神zy!

很多人的第一想法就是选前need小的白边,再放黑边,很不幸这是错的难过

正确解法很有启发性。

如果我们按照正常的选边来做最小生成树,如果选出的白边大于need条,我们就需要少选一些的白边,否则我们就需要多选一些白边。

如何才能少选一些白边呢?我们可以该白边全部加上一个正数,同理我么可以多选一些白边。

这样我们就可以二分了!

但是这还有一些问题,假设我们二分得到x,其对应的生成树大小为need+1,而x+1对应的大小为need-1,答案是什么?

其实答案就是x下生成树大小减去need*x

为什么?

考虑被删除的那两条边,由于在值+1后被删除,一定有相等大小的黑边可以代替此白边,也就是说我们可以恰好选出need条白边。

得证!

再跪大神zy!

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
const int Maxn = 100005;
int fa[Maxn];
int n,m,need;
int L,R,Mid;
int ans,sum,K;
int m1,m2,i;
struct EDGE
{
  int x,y,len,col;
  void read()
  {
    scanf("%d%d%d%d",&x,&y,&len,&col);
    x++; y++;
  }
  bool operator <(const EDGE &a)const
  {
    return (len < a.len) || (len == a.len && col<a.col);
  }
} edge[Maxn], e[Maxn];

int gf(int x){
  int xx=x, xxx;
  while (xx!=fa[xx]) xx=fa[xx];
  while (x!=xx) xxx=x, x=fa[x], fa[xxx]=xx;
  return xx;
}

bool Judge(int dlt){
  for (i=1;i<=m;i++){
    e[i] = edge[i];
    if (e[i].col==0) e[i].len += dlt;
  }
  sort(e+1,e+m+1);
  sum = 0; K = 0;
  for (i=1;i<=n;i++) fa[i] = i;
  for (i=1;i<=m;i++){
    m1 = gf(e[i].x);
    m2 = gf(e[i].y);
    if (m1!=m2){
      fa[m1] = m2;
      sum += e[i].len;
      if (e[i].col==0) K++;
    }
  }
  return K >= need;
}

int main(){
  freopen("2654.in","r",stdin);
  freopen("2654.out","w",stdout);
  scanf("%d%d%d",&n,&m,&need);
  for (i=1;i<=m;i++)
    edge[i].read();
  L = 0; R = 200;
  while (L<=R){
    Mid = (L+R)/2 - 100;
    if (Judge(Mid)){
      L = Mid+100 + 1;
      ans = sum - Mid*need;
    }
    else R = Mid+100 - 1;
  }
  printf("%d\n",ans);
  return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值