#3696 tree【最小生成树】

版权声明:----------------------------------------转载是ok的,但要附上出处哟 https://blog.csdn.net/qq_43040655/article/details/87122888

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

输入
第一行V,E,need分别表示点数,边数和需要的白色边数。

接下来E行

每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色)

输出
一行表示所求生成树的边权和。

样例输入 [复制]
2 2 1
0 1 1 1
0 1 2 0
样例输出 [复制]
2

数据规模和约定
10%:V<=10

30%:V<=15

100%:V<=50000,E<=100000

所有数据边权为[1,100]中的正整数。

思路:

只是 最小生成树 无法控制 白边 的 取量
朴素地方法是 取 k 条白边 的 所有情况 枚举一边 复杂度。。

巧妙的思路: 每次给 所有 白边加上一个 值, 跑最小生成树,白边只可能减少 不会增加
每次给 所有 白边 减上一个值, 跑最小生成树 ,白边只能增加,不会减少
控制白边与黑边的相对取舍
只需要二分加减的数即可(注意数据规模)

注: 二分 时 策略尽量选白边, 所以得出的答案含义: 至少 选 k 条白边。

#include<queue>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<iomanip>
#include<cmath>
#include<string>
#include<algorithm>
#define pf printf
#define sf scanf
using namespace std;
const int maxn=5e4+10,maxm=1e5+10;
int fa[maxn];
inline int find(int u){
	int uu=u,tu;
	while(fa[u]!=0){
		u=fa[u];
	}
	while(uu!=u){
		tu=fa[uu];
		fa[uu]=u;
		uu=tu;
	}
	return u;
}
struct edge{
	int u,v,w,k;
	bool operator <(const edge &t) const {
		if(w==t.w)return k<t.k;
		return w<t.w;
	}
}e[maxm],ori[maxm];
int n,m,ans=0,need;
inline bool check(int mid){
	memcpy(e,ori,sizeof ori);
	for(int i=1;i<=m;++i)if(!e[i].k)e[i].w+=mid;
	memset(fa,0,sizeof fa);
	sort(e+1,e+m+1);
	int tot=0,all=0;ans=0;
	for(int i=1;i<=m;++i){
		int fx=find(e[i].u),fy=find(e[i].v);
		if(fx!=fy){
			fa[fx]=fy;
			ans+=e[i].w;
			++tot;
			all+= e[i].k==0 ;
		}
		if(tot==n-1)break;
	}
	if(all>=need) return 1;
	else return 0;
}

signed main(){
	sf("%d%d%d",&n,&m,&need);
	for(int i=1;i<=m;++i){
		int a,b,c,d;scanf("%d%d%d%d",&a,&b,&c,&d);
		++a,++b;
		ori[i]=(edge){a,b,c,d};
	}
	int l=-1e3,r=1e3,mid;
	while(l+1<r){
		mid=(l+r)>>1;
		if(check(mid))l=mid;
		else r=mid;
	}
	if(check(r))printf("%d",ans-need*r);
	else{
		check(l);printf("%d",ans-need*l);
	}	
	return 0;
}


猜你喜欢

转载自blog.csdn.net/qq_43040655/article/details/87122888
今日推荐