P2619 [国家集训队]Tree I

二分答案。

考虑边肯定还是根据边权从小到大加入。
显然我们不能先加一堆白边。
但是我们可以通过白边的边权控制加入最小生成树的白边数量。

那么数量满足单调性可以二分。

#include<bits/stdc++.h>
using namespace std;
#define orz cout<<"lytcltcltcltcltcltcl"<<endl
inline int r(){int s=0,k=1;char c=getchar();while(!isdigit(c)){if(c=='-')k=-1;c=getchar();}while(isdigit(c)){s=s*10+c-'0';c=getchar();}return s*k;}
int n,m,need,tot,fa[1000001];
struct node
{
	int from,to,dis,col;
}a[1000001];
bool cmp(node x,node y)
{
	if(x.dis==y.dis)return x.col<y.col;
	return x.dis<y.dis;
}
int father(int x)
{
	if(fa[x]!=x)fa[x]=father(fa[x]);
	return fa[x];
}
bool check(int x)
{
	tot=0;
	int now=0,whi=0;
	for(int i=1;i<=m;i++)
	if(a[i].col==0)a[i].dis+=x;
	sort(a+1,a+m+1,cmp);
	for(int i=1;i<=n;i++)fa[i]=i;
	for(int i=1;i<=m;i++)
	{
		int x=a[i].from,y=a[i].to;
		int fax=father(x),fay=father(y);
		if(fax==fay)continue;
		
		fa[fax]=fay;
		tot+=a[i].dis;
		if(a[i].col==0)whi++;
		now++;
		if(now==n-1)break;
	}
	for(int i=1;i<=m;i++)
	if(a[i].col==0)a[i].dis-=x;
	if(whi>=need)return 1;
	return 0;
}
int main()
{
	n=r();m=r();need=r();
	for(int i=1;i<=m;i++)
	{
		a[i].from=r()+1;
		a[i].to=r()+1;
		a[i].dis=r();
		a[i].col=r();
	}
	int l=-200,r=200,ans1=0,ans2=0;
	while(l<=r)
	{
//		cout<<l<<" "<<r<<endl;
		int mid=(l+r)/2;
		if(check(mid))l=mid+1,ans1=mid,ans2=tot;//太多 
		else r=mid-1;
	}
	cout<<ans2-ans1*need;
}
posted @ 2021-09-03 17:33  lei_yu  阅读(36)  评论(0编辑  收藏  举报