BZOJ 2654 tree

来源:互联网 发布:js滚轮时间选择控件 编辑:程序博客网 时间:2024/06/05 20:15

先给出一种方案:
首先二分一个权值mid,然后给白边每一个边加上mid,求一个最小生成树,观察白边使用的个数,二分到白边等于need,ans=val-mid*need。
下面给出证明:
白边数是一定的,二分权值,mid大,显然选的白边数会减少,具有可二分性。
在研究ans的单调性:假设现在有两种情况,白边数均为need,且mid1<mid2在假设给情况1的白色边权值都加上Δmid,而选出的边不变,那么必然不会更优
(二者的树形可能都会不一样,树形改变则一定不会比原来更差),于是ans1<=valΔmidmid+Δmid=ans2

(胡证的啊我也不知道怎么证,好捉急啊。。。)

#include<iostream>#include<cstdlib>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn=50005;struct edge{    int x,y,d,val;    bool operator < (const edge &x)const    {        return val<x.val||(val==x.val&&d<x.d);    }}e[maxn<<1];int n,m,need,sum;int fa[maxn];inline int find(int x){    return x==fa[x]?x:fa[x]=find(fa[x]);}bool judge(int mid){    int num=0;    sum=0;    for(int i=1;i<=n;i++)fa[i]=i;    for(int i=1;i<=m;i++)if(!e[i].d)        e[i].val+=mid;    sort(e+1,e+m+1);    for(int i=1;i<=m;i++)    {        int x=find(e[i].x);        int y=find(e[i].y);        if(x==y)continue;        fa[x]=y;        if(!e[i].d)num++;        sum+=e[i].val;    }    for(int i=1;i<=m;i++)if(!e[i].d)        e[i].val-=mid;    return num>=need;}int main(){    scanf("%d%d%d",&n,&m,&need);    for(int i=1;i<=m;i++)    {        scanf("%d%d%d%d",&e[i].x,&e[i].y,&e[i].val,&e[i].d);        e[i].x++;e[i].y++;    }    int l=-105,r=105,ans;    while(l<r)    {        int mid=l+r>>1;        if(judge(mid))            ans=sum-mid*need,l=mid+1;        else r=mid;    }    printf("%d",ans);    return 0;}