[bzoj 2654] tree

来源:互联网 发布:魔法王座神翼升阶数据 编辑:程序博客网 时间:2024/06/04 18:21

跪神题,跪秒题大神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
原创粉丝点击