P2619 [国家集训队2]Tree I 整体二分(~~~~~~~~)个鬼嘞

P2619 [国家集训队2]Tree I

汗,此整体非彼整体2333

这是一道二分题不假。而是将整体的位置进行二分

偶然时看到这个题。

百思不得其解,然后网上一查。恍然大悟呀

大体就是说(使用克鲁斯卡尔)。如果选了多于need条白边,就将所有白边的边权加上一个数,如果小于need条,就将所有白边的权值减去一个数

然后发现,这是具有单调性的。所以我们二分这个减去或加上的数就可以。

不过为什么不直接贪心的选取need条白边呢?

钟梓俊
貌似网上的题解现在还没有给出具体的证明和数据,那么我就来发一下。
如果我们选了 needneed 条较小的白边,那么可能会影响黑边的选择。下面是一组可以卡掉直接选need条白边的数据,如下。

3 4 1
0 1 5 0
1 2 6 0
0 1 2 1
1 2 9 1
#include<cstdio> 
#include<iostream>
#include<cstring>
#include<algorithm>
using std::sort;
const int maxn=101000;
struct node
{
	int p1,p2;
	int weight;
	int c;
	bool operator <(const node &a)const 
	{
		if(weight==a.weight)	return c<a.c;
		return weight<a.weight;
	}
};
node line[maxn];
node copy[maxn];
int f[maxn];
int v,e,N,ans;
int find(int x)
{
	return f[x] == x ? x : f[x]=find(f[x]) ;
}
bool check(int val)
{
	ans=0;
	memset(f,0,sizeof(f));
	for(int i=0;i<=v;i++)	f[i]=i;
	for(int i=1;i<=e;i++)
	{
		copy[i]=line[i];
		if(!line[i].c)	copy[i].weight+=val;
	}//整体加上一个数
	sort(copy+1,copy+1+e);
	int get=0,tot=0,i=1;
	while(get<v-1)//普通的克鲁斯卡尔
	{
		int f1=find(copy[i].p1),f2=find(copy[i].p2);
		if(f1!=f2)
		{
			get++;
			ans+=copy[i].weight;
			f[f1]=f2;
			if(!copy[i].c)	tot++;
		}
		i++;
	}
	return tot>=N;
}
int main()
{
	scanf("%d%d%d",&v,&e,&N);
	for(int i=1;i<=e;i++)
		scanf("%d%d%d%d",&line[i].p1,&line[i].p2,&line[i].weight,&line[i].c);
	int l=-101,r=101,end;
	while(l<=r)
    {
        int mid=(l+r)/2;
        if(check(mid))
			l=mid+1,end=mid;
        else r=mid-1;
    }
    check(end);
    printf("%d
",ans-N*end);//最后减去
}