【BZOJ】【P2654】【tree】【题解】【二分+MST】

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2654

考虑把白色边权加上一个值x,那么最小生成树中的白色边数量的可行最大值g(x)与可行最小值f(x)随x增大而减小,然后就可以二分一个x使得need∈[f(x),g(x)]

Code:

#include<bits/stdc++.h>
#define fst first
#define sec second
using namespace std;
const int maxn=50010;
int ans=0;
struct edge{
	int u,v,w,_w,c;
}edges[int(1e5)+5];int F;
bool cmp(edge a,edge b){return a.w!=b.w?a.w<b.w:(F==1?a.c<b.c:a.c>b.c);}
int fa[maxn];
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
int n,m,ned;
int cnt[101];
int cnt_dw[101],cnt_up[101];
typedef pair<int,int> par;
map<int,int>tmp,whi;
par Mst(){
	for(int i=1;i<=n;i++)fa[i]=i;int k=0;
	sort(edges+1,edges+1+m,cmp);int res=0,wh=0;
	for(int i=1;i<=m&&k<n-1;i++){
		int u=edges[i].u,v=edges[i].v;
		if(find(v)!=find(u)){
			fa[find(v)]=find(u);k++;
			wh+=edges[i].c==0;res+=edges[i].w;
		}
	}return par(wh,res);
}
int main(){
	scanf("%d%d%d",&n,&m,&ned);
	for(int i=1;i<=m;i++){
		int u,v,w,c;
		scanf("%d%d%d%d",&u,&v,&w,&c);
		u++;v++;
		edges[i].u=u;
		edges[i].v=v;
		edges[i]._w=w;
		edges[i].w=w;
		edges[i].c=c;
		cnt[w]++;
	}int l=-101,r=101;
	while(l<r){
		int mid=(l+r)>>1;
		for(int i=1;i<=m;i++)if(!edges[i].c)edges[i].w=edges[i]._w+mid;
		par up,dw;
		F=1;up=Mst();
		F=0;dw=Mst();
		if(dw.fst<=ned&&ned<=up.fst){
			ans=Mst().sec;
			ans-=ned*mid;break;
		}
		if(ned>up.fst)
			r=mid;
		else l=mid+1;
	}
	cout<<ans<<endl;
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值