Luogu P2619 [国家集训队2]Tree I

题目描述

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

题目保证有解。

题目链接


思路

其实这题我好像在7月份就在扬州听过

思想没有问题,但由于当时太弱了,没能做出来

所以清flag时先挑一个软柿子捏

将每一个白边加上一个权值,跑kruskal,二分此权值,直到生成树正好有need条白边。


code

#include<bits/stdc++.h>
using namespace std;
const int MAXN=100000+10;
inline int read()
{
	int s=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*f;
}

struct node{
	int from,to,v;
	bool color;
}_map[MAXN],edge[MAXN];

int v,e,need,cnt,num,sum,ans;
int fa[MAXN];

void addedge(int s,int t,int c,int col)
{
	edge[++cnt].color=col;
	edge[cnt].from=s;
	edge[cnt].to=t;
	edge[cnt].v=c;
}

bool cmp(node a,node b)
{
	return (a.v==b.v)? a.color<b.color : a.v<b.v;
}

int find(int x)
{
	if(fa[x]==x)	return x;
	return fa[x]=find(fa[x]);
}

void kruskal()
{
	for(int i=1;i<=v;++i){
		fa[i]=i;
	}
	num=0,sum=0;
	int nn=0;
	sort(edge+1,edge+e+1,cmp);
	for(int i=1;nn<v-1;++i){
		int xx=find(edge[i].from),yy=find(edge[i].to);
		if(xx==yy){
			continue;
		}
		if(xx!=yy){
			nn++;
			fa[xx]=yy;
			if(!edge[i].color){
				num++;
			}
			sum+=edge[i].v;
		}
	}
}

bool check(int mid)
{
	memcpy(edge,_map,sizeof(_map));
	for(int i=1;i<=e;++i){
		if(!edge[i].color){
			edge[i].v+=mid;
		}
	}
	kruskal();
	return num>=need;
}

void midfind()
{
	int mid,l=-120,r=120;
	while(l<=r){
//		cout<<"midfind\n";
		mid=(l+r)/2;
		if(check(mid)==1){
			ans=sum-mid*need;
			l=mid+1;
		}else{
			r=mid-1;
		}	
	}	
}

int main()
{
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
	cin>>v>>e>>need;
	for(int i=1;i<=e;++i){
		int s,t,c,col;
		s=read(),t=read(),c=read(),col=read();
		addedge(s+1,t+1,c,col);
	}
	memcpy(_map,edge,sizeof(_map));
	midfind();
	cout<<ans;
	return 0;
}
posted @ 2019-09-20 22:04  zhu_chen  阅读(139)  评论(0编辑  收藏  举报