bzoj2654 tree

题目链接:bzoj2654
题目大意:
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。
题目保证有解。

题解:
二分+MST
诶呀,题解好机智,,,
如果把所有白色边都减去一个delta后得到一个最小生成树的话,那么可以发现,这个生成树中的白色边是根据delta的增大而不降的,可以说是有单调性的。于是就二分这个delta,每次跑最小生成树check就好了。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
#define maxn 50100
#define maxm 101000

const double inf=20000000;
struct node
{
    int x,y,b;double c;
}a[maxm];
bool cmp(node x,node y) 
{
    if (x.c!=y.c) return x.c<y.c;
    return x.b<y.b;
}
int n,m,need,fa[maxn];
int ffind(int x){return (x==fa[x])?x:fa[x]=ffind(fa[x]);}
double twdiv(double delta)
{
    int i,cnt=0,num=0;double ans=0;
    for (i=1;i<=n;i++) fa[i]=i;
    for (i=1;i<=m;i++) if (!a[i].b) a[i].c-=delta;
    sort(a+1,a+m+1,cmp);
    for (i=1;i<=m;i++)
    {
        int fx=ffind(a[i].x),fy=ffind(a[i].y);
        if (fx!=fy)
        {
            fa[fx]=fy;cnt++;
            if (!a[i].b) num++;
            ans+=a[i].c;
            if (cnt==n-1) break;
        }
    }
    for (i=1;i<=m;i++) if (!a[i].b) a[i].c+=delta;
    if (num>=need) return ans;
    return -inf;
}
int main()
{
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    int i,x,y,b,c;double ans,det;
    scanf("%d%d%d",&n,&m,&need);

    for (i=1;i<=m;i++)
    {
        scanf("%d%d%d%d",&x,&y,&c,&b);
        x++;y++;a[i].x=x;a[i].y=y;a[i].c=c;a[i].b=b;
    }
    ans=det=0;
    double l,r,mid;
    l=-100;r=100;
    while (l<r)
    {
        mid=(l+r)/2;
        double now=twdiv(mid);
        if (now==-inf) l=mid+1;
        else {r=mid-1;ans=now;det=mid;}
    }
    printf("%d\n",(int)(ans+det*need));
    return 0;
}
posted @ 2017-03-08 21:51  OxQ  阅读(153)  评论(0编辑  收藏  举报