[bzoj2654]tree(二分+最小生成树)

题目:

我是超链接

题解:

一开始博主想要把黑白边分开,然后sort,选择前need小的白边连起来,然后再连黑边
但这个贪心是不对的,因为黑边的大小不确定,如果目前选择了较小的白边,可能会选上更大的黑边

问题的关键在于求出的最小生成树不一定含有need条白边
1、如果白边 > need条,我们需要“拖累”白边,让ta不要加进去那么多,你光拖白边A,不拖白边B就很不公平啊,所以要拖累就把所有的白边全都拖累上
2、如果白边 < need条,我们需要“帮助”这些白边,让ta加进去的更多一些,那就给ta们全都减去一个值
我们到底是帮助还是拖累呢?具体帮多少呢?我们可以运用二分!

你可以发现这是单调的:你加入的值越多,可以加入的白边就越少;反之亦然
那就走起?最后的答案就是 sum-你添加的总值

下面是一些小细节:
当有一种情况,mid+1加入的白边少1,mid加入的白边多1,这要怎么办呢?这样的情况只会出现于一些白边和黑边的权值相等,我们在排序的时候,权值相等的白边在先就OK啦

以下为博主口胡,即使不注意这个,你依然可以过这道题
但你的程序不一定是对的,试一下(卡掉学姐的)这组数据
3 3 1
0 1 4 1
0 2 2 0
1 2 2 0
ans=6
一个小问题:你的ans值什么时候修改呢?我们说你不能对于ans取min值(why?)
如果你把这个语句(ans=min(ans,sum-need*mid))放在[选的白边小于need]的后面,你就不怕一上来白边就小于mid,选了一棵正常的最小生成树,以后的权值肯定不会被修改
如果你把这个语句放在[选的白边大于need]的后面,你就不怕一上来白边大于mid,就同上了(hack了学姐。。。)
事实上可以证明当我们限定白边的数量一定的时候,答案也是唯一的
可以避免这个问题:只有在白点相等的时候再修改ans值
但是这样的答案是错的?
为什么呢?因为这道题的题目是错的,ta其实想让你求让你求一棵最小权的至少有need条白色边的生成树。
发现这个问题之后,以上就全部是博主的自我揣测了

代码:

#include <cstdio>
#include <algorithm>
#include <cstring>
#define INF 1e9
using namespace std;
struct hh{int x,y,c,col;}e[100005],b[100005];
int f[50005],n,m,ne,sum;
int cmp(hh a,hh b){if (a.c==b.c)return a.col<b.col;else return a.c<b.c;}
int find(int x)
{
    if (f[x]!=x) f[x]=find(f[x]);
    return f[x];
}
int check(int mid)
{
    int i,tot=0,size=0;sum=0;
    for (i=0;i<n;i++) f[i]=i;
    for (i=1;i<=m;i++)
    {
        b[i]=e[i];
        if (e[i].col==0) b[i].c+=mid;
    }
    sort(b+1,b+m+1,cmp);
    for (i=1;i<=m;i++)
      {
        int a=find(b[i].x),c=find(b[i].y);
        if (a!=c)
        {
            f[a]=c;
            tot++;sum+=b[i].c;
            if (!b[i].col) size++; 
        }
        if (tot==n-1) break;
      }
    return size;
}
int main()
{
    int i,ans=INF;
    scanf("%d%d%d",&n,&m,&ne);
    for (i=1;i<=m;i++) scanf("%d%d%d%d",&e[i].x,&e[i].y,&e[i].c,&e[i].col);
    int l=-100,r=100;
    while (l<=r)
    {
        int mid=(l+r)>>1;
        if (check(mid)<ne) r=mid-1;//加入的边太少了,需要减的值很多啊 
        else l=mid+1,ans=sum-mid*ne;
    }
    printf("%d",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值