[FROM WOJ]#3696 tree(BZOJ 2654)

#3696 tree

二分是个好东西

题面
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。 题目保证有解。
数据规模和约定 ·10%:V<=10
30%:V<=15
100%:V<=50000,E<=100000
所有数据边权为[1,100]中的正整数。

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

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

样例输入
2 2 1
0 1 1 1
0 1 2 0

样例输出
2

SOL
直接kruskal去求MST显然不成立,那么如何处理白边便成为一个有趣的问题
考虑将边权排个序——排一下白边,然后排一下黑边,然后加最小的need条白边和n-1-need条黑边得到最小权。这是一种简单又高效的贪心——但这是错误的做法。我们仔细想想kruskal的原理就会发现,这种贪心不能保证加完边一定能生成一棵树——因为有重边,所以你即使加了n-1条边,也不见得能生成树。
于是就有了一个奇怪而又正确的做法——二分答案+kruskal!
当你直接kruskal后,你会发现答案在绝大多数情况下都是错的,这时候你就想去掉多出的白边或者加入缺少的白边来维护最小权,这意味着你要去掉其中的一些黑边。那么怎么去掉呢?你会发现,如果你给所有白边边权适当地加上一些,生成树里的白边就会变少,反之你可以使生成树里的白边变多,于是你可以二分一下这个delta的值,最后再去掉这个delta就行了。

代码:

#include<bits/stdc++.h>
#define N 50505
#define ll long long
int n,m,k,sum,ecnt,ans;
using namespace std;
inline int rd(){
    int data=0;int w=1; static char ch=0;ch=getchar();
    while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
    if(ch=='-') w=-1,ch=getchar();
    while(ch>='0' && ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
    return data*w;
}
struct node{int u,v,w,col;}e[N<<1];
inline void add(int u,int v,int w,int col){
	e[++ecnt].u=u;
	e[ecnt].v=v;
	e[ecnt].w=w;
	e[ecnt].col=col;
}
int f[N];
int find(int x){return (f[x]==x)? x : f[x]=find(f[x]);}
inline bool cmp(node a,node b){if(a.w==b.w)return a.col<b.col;return a.w<b.w;} 
inline bool kruskal(int delta){
	int line=0,cnt=0; sum=0;
	for(int register i=1;i<=n;i++)f[i]=i;
	for(int register i=1;i<=m;i++)
		if(!e[i].col)e[i].w+=delta;
	sort(e+1,e+m+1,cmp);
	for(int register i=1;cnt!=n-1&&i<=m;i++){
		int fx=find(e[i].u),fy=find(e[i].v);
		if(fx!=fy){
			f[fx]=fy;
			cnt++;
			sum+=e[i].w;
			if(!e[i].col)line++;
		}
	}
	for(int register i=1;i<=m;i++)if(!e[i].col)e[i].w-=delta;
	return line>=k;
}
int main(){
	n=rd();m=rd();k=rd();
	for(int register i=1;i<=m;i++){
		int s=rd(),t=rd(),c=rd(),col=rd();
		add(s+1,t+1,c,col);
	}
	int l=-101,r=101,mid;
	while(l<=r){
		mid=l+r>>1;
		if(kruskal(mid)){
			l=mid+1;
			ans=sum-k*mid;		
		}
		else r=mid-1;
	}
	printf("%d",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/hzq_oi/article/details/87093792