bzoj2654 一道神奇的最小生成树

哇这真是一道可怕的题目。。

首先我们发现,由于题目保证有解,放缩白边的边权,一定可以找到一个值使最小生成树里恰好有need条白边,答案一定是在这些最小生成树的边权和里面找的。

然而有一个非常严肃的问题。。减去的白边边权最后还是要加上的啊orz。。于是我们在二分的时候,希望求出至多能够放多少条白边,如果最多也只能放need条了那才行啊,不然本来可以用黑边(避免权值被加回来)的地方放了白边,看起来是有need条了,其实权值和不是最小,会出事的。那么二分减去的权值的时候,如果至多放的白边数>=need(可能减去的太多了),则r=mid;反之,l=mid+1。

总而言之一句话,至多放的白边数才是真的白边数,不然白边必须用黑边替代掉的。

然后怎么求至多放的白边数目呢,排序的时候把颜色设为第二关键字就可以了。(能选白边就优先选白边)

#include<bits/stdc++.h>
using namespace std;
#define rep(x,y,z) for (int x=y; x<=z; x++)
#define downrep(x,y,z) for (int x=y; x>=z; x--)
#define ms(x,y,z) memset(x,y,sizeof(z))
#define LL long long
#define repedge(x,y) for (int x=hed[y]; ~x; x=edge[x].nex)
inline int read(){
	int x=0; int w=0; char ch=0;
	while (ch<'0' || ch>'9') w|=ch=='-',ch=getchar();
	while (ch>='0' && ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return w? -x:x;
}
const int maxn=50005;
const int maxm=100005;
int f[maxn],vis[maxm],n,m,need;
struct node{ int u,v,len,col; }e[maxm];
bool operator < (node a,node b){ return (a.len==b.len)? (a.col<b.col):(a.len<b.len); }
int gf(int k){ return (k==f[k])? k:f[k]=gf(f[k]); }
int Kruskal(){
	ms(vis,0,vis); rep(i,1,n) f[i]=i;
	sort(e+1,e+m+1); int sum=0;
	rep(i,1,m){
		int fa=gf(e[i].u); int fb=gf(e[i].v);
		if (fa==fb) continue; 
		sum+=e[i].len; vis[i]=1; f[fa]=fb;
	}
	return sum;
}
int check(int len){ 
    rep(i,1,m) if (e[i].col==0) e[i].len-=len;
    int tmp=Kruskal(); int sum=0;
    rep(i,1,m) if (e[i].col==0){ e[i].len+=len; sum+=vis[i]; }
    return (sum>=need);
}
int solve(){
	int l=-100; int r=100;
	while (l<r){
		int mid=((l+r)>>1); 
		if (check(mid)) r=mid;
		else l=mid+1;
	}
	return l;
}
int main(){
	n=read(); m=read(); need=read();
	rep(i,1,m) e[i].u=read()+1,e[i].v=read()+1,e[i].len=read(),e[i].col=read();
    int todec=solve(); rep(i,1,m) if (e[i].col==0) e[i].len-=todec;  
    int ans=Kruskal(); rep(i,1,m) if (e[i].col==0) e[i].len+=todec; ans+=todec*need;
    printf("%d\n",ans); return 0;
}

猜你喜欢

转载自blog.csdn.net/shiveringkonnyaku/article/details/82797965