您的位置:首页 > 其它

bzoj2654 tree 最小生成树+二分验证

2016-12-20 21:25 316 查看
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。

因为随着白色边权值同时增大或同时减小,最小生成树中白色数量也会增大或减小,所以可以二分一个值Mid,白色边同时加上Mid,求生成树中白边数量。然后调整上下界。

#include<iostream>
#include<cstdio>
#include<algorithm>
#define maxn 50005
using namespace std;
struct E{
int f,t,d,c;
void read()
{scanf("%d%d%d%d",&f,&t,&d,&c);}
}b[maxn<<1];
bool cmp(E A,E B)
{
if(A.d!=B.d)
return A.d<B.d;
return A.c<B.c;
}
int F[maxn];
int find(int x)
{return F[x]==x?x:F[x]=find(F[x]);}
int n,m,d;
bool kul()
{
for(int i=0;i<=n;i++)
F[i]=i;
sort(b+1,b+m+1,cmp);
int cnt=0,tot=0;
for(int i=1;i<=m;i++)
{
int f=b[i].f,t=b[i].t;
if(find(f)!=find(t))
{
F[find(f)]=find(t);tot++;
if(!b[i].c) cnt++;
}
}
if(cnt>=d&&tot==n-1)
return true;
return false;
}
int main()
{
scanf("%d%d%d",&n,&m,&d);
for(int i=1;i<=m;i++)
b[i].read();
int l=-101,r=101;
while(r-l>1)
{
int mid=(l+r)>>1;
for(int i=1;i<=m;i++)
if(!b[i].c)b[i].d+=mid;
if(kul()) l=mid;
else r=mid;
for(int i=1;i<=m;i++)
if(!b[i].c)b[i].d-=mid;
}
for(int i=1;i<=m;i++)
if(!b[i].c) b[i].d+=l;
sort(b+1,b+m+1,cmp);
for(int i=0;i<=n;i++)
F[i]=i;
int ans=0;
for(int i=1;i<=m;i++)
{
int f=b[i].f,t=b[i].t;
if(find(f)!=find(t))
{
F[find(f)]=find(t);
ans+=b[i].d;
}
}
ans-=d*l;
cout<<ans;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: