BZOJ 2654 tree(二分答案+最小生成树)

本文介绍了一种解决特定最小生成树问题的方法,即在考虑边的颜色限制下找到权值最小且包含指定数量白色边的生成树。通过调整白色边的权重并使用Kruskal算法,结合二分搜索技巧来确定最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2654: tree

Description

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

Input

第一行V,E,need分别表示点数,边数和需要的白色边数。
接下来E行,每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色)。

Output

一行表示所求生成树的边权和。
V<=50000,E<=100000,所有数据边权为[1,100]中的正整数。

Sample Input

2 2 1
0 1 1 1
0 1 2 0

Sample Output

2

思路:
显而易见,这是一道最小生成树......
但是问题在于其中有黑白边及白边大小上的要求。当然我们能够发现,在忽略限制条件下求出的最小生成树里面的所有黑
边,也会在我们希望求的,有条件限制下的生成树中。接着我们知道,当最小生成树中白边数量超出我们的需求时,我们
会删去若干白边,并继续顺序加黑边。此,我们要做到能够控制白边数量,根据kruskal的做法,我们发现选边的优先极
取决于边权,如果我们能够将白边的边权进行扩大,那么就可以做到控制白边数量了。接着,思考:当把所有白边都加上
一个偏移量offset之后,可以求出一棵最小生成树,再将答案减去树中所有白边的数量*offset,其实这就是一种合法的生
成树。
在选择不同的offset时,白边被偏移的大小使得我们能够控制白边进入树中的数量。offset过大导致选择的白边太少,反之
则太多。我们发现白边数量随offset的递增而递增,因此我们可以二分答案,二分offset的大小求解。
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int MAXE=100010;
struct edge
{
	int u,v,w,c;
}E[MAXE];
int v,e,need,wn,ans,tot;
int f[MAXE/2];
int find(int x)
{
	return f[x]==x?x:f[x]=find(f[x]);
}
bool cmp(edge a,edge b)
{
	return a.w==b.w?a.c<b.c:a.w<b.w;
}
int kruskal(int add)
{
	for(int i=1;i<=v;i++)
		f[i]=i;
	for(int i=1;i<=e;i++)
	{
		if(!E[i].c)
		{
			E[i].w+=add;
		}
	}
	sort(E+1,E+1+e,cmp);
	int cnt=0,w=0;tot=0;
	for(int i=1;i<=e;i++)
	{
		if(find(E[i].u)!=find(E[i].v))
		{
			f[find(E[i].u)]=find(E[i].v);
			cnt++;tot+=E[i].w;
			if(!E[i].c)w++;
		}
		if(cnt==v-1)break;
	}
	for(int i=1;i<=e;i++)
	{
		if(!E[i].c)E[i].w-=add;
	}
	return w;
}
int main()
{
	cin>>v>>e>>need;
	for(int i=1;i<=e;i++)
	{
		cin>>E[i].u>>E[i].v>>E[i].w>>E[i].c;
		E[i].u++;E[i].v++;
		if(!E[i].c)wn++;
	}
	int l=-100,r=100;
	while(l<r)
	{
		int mid=(l+r)>>1;
		if(kruskal(mid)>=need){l=mid+1;ans=tot-wn*mid;}
		else r=mid-1;
	}
	cout<<ans<<endl;
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值