BZOJ2654 tree

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

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

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

Sample Input
2 2 1
0 1 1 1
0 1 2 0
Sample Output
2
HINT
数据规模和约定
0:V<=10
1,2,3:V<=15
0,..,19:V<=50000,E<=100000
所有数据边权为[1,100]中的正整数。

题解
给白色边都加上一个权值,做mst会使得选取的白边数量减少(但是当黑白边权值一样,应该优先选择白边,因为在当前的设定下,是可以选择出若干条白边满足要求的),所以可以二分这个权值

#include<iostream>
#include<set>
#include<map>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<vector>
#include<queue>
#include<algorithm>
#include<cmath>
#define inf 1000000000
#define pa pair<int,int>
#define ll long long 
using namespace std;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m,cnt,tot,ned;
int sumv;
int u[100005],v[100005],w[100005],c[100005];
int fa[100005];
struct edge{
	int u,v,w,c;
}e[100005];
bool operator<(edge a,edge b) //白边尽量放在前面,坑点
{
	return a.w==b.w?a.c<b.c:a.w<b.w;
}
int find(int x)
{
	return x==fa[x]?x:fa[x]=find(fa[x]);
}
bool check(int x)
{
	tot=cnt=0;
	for(int i=1;i<=n;i++)
	    fa[i]=i;
	for(int i=1;i<=m;i++)
	{
		e[i].u=u[i],e[i].v=v[i],e[i].w=w[i];e[i].c=c[i];
		if(!c[i]) //强行给白边加上一个权值 
		    e[i].w+=x;
	}
	sort(e+1,e+m+1);
	for(int i=1;i<=m;i++)
	{
		int p=find(e[i].u),q=find(e[i].v);
		if(p!=q)
		{
			fa[p]=q;
			tot+=e[i].w;
			if(!e[i].c) //统计白边有多少条 
			    cnt++;
		}
	}
	return cnt>=ned;
}
int main()
{
	n=read();m=read();ned=read();
	for(int i=1;i<=m;i++)
	{
		u[i]=read(),v[i]=read(),w[i]=read(),c[i]=read();
		//w[i]为边权,c[i]为color ,(0白色1黑色)
		
		u[i]++;
		v[i]++;
	}
	int l=-105,r=105;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(check(mid))
		    l=mid+1,
			sumv=tot-ned*mid;//sumv为实际上求出来的mst的代价 
		else 
		     r=mid-1;
	}
	printf("%d",sumv);
	return 0;
}

  

原文地址:https://www.cnblogs.com/cutemush/p/12464295.html