2654: tree

Time Limit: 30 Sec Memory Limit: 512 MB
Submit: 4829 Solved: 2093
[Submit][Status][Discuss]
Description
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。
题目保证有解。
Input
第一行V,E,need分别表示点数,边数和需要的白色边数。
接下来E行,每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色)。
Output
一行表示所求生成树的边权和。
V<=50000,E<=100000,所有数据边权为[1,100]中的正整数。
Sample Input
2 2 1

0 1 1 1

0 1 2 0
Sample Output
2


wqs它的作用就是题目给了一个选物品的限制条件,要求刚好选m个,让你最大化(最小化)权值,
然后其特点就是当选的物品越多的时候权值越大(越小)。

然后这类题目,我们就可以wqs二分啦,对于这道题,我们就是给我们白色的边,减去一个权值,使得白色边具有更高的优先级,然后统计答案时加上即可。

wqs的正确性可以用凸函数来计算。

注意不能直接选need条需要的最小白色边,因为白色边会影响到黑色边的选取。

还有统计答案时只需要加上need边的权值。


AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=1e5+10;
int f[N<<1],n,m,need,cnt,res,sum,num;
struct node{int u,v,w,col;}t[N];
int cmp(node a,node b){return a.w==b.w?a.col<b.col:a.w<b.w;}
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
int kruskal(int mid){
	for(int i=1;i<=n;i++)	f[i]=i;
	for(int i=1;i<=m;i++)	if(!t[i].col)	t[i].w-=mid;
	sort(t+1,t+1+m,cmp);	int cnt=0; sum=num=0;
	for(int i=1;i<=m&&cnt<n-1;i++){
		int fa=find(t[i].u); int fb=find(t[i].v);
		if(fa!=fb){
			cnt++;	sum+=t[i].w;	f[fa]=fb;	num+=(!t[i].col);
		}
	}
	for(int i=1;i<=m;i++)	if(!t[i].col)	t[i].w+=mid;
	return num;
}
int wqs(){
	int l=-100,r=100;
	while(l<r){
		int mid=l+r>>1;
		if(kruskal(mid)>=need){
			res=sum+need*mid;	r=mid;
		}
		else	l=mid+1;
	}
	return res;
}
signed main(){
	cin>>n>>m>>need;
	for(int i=1;i<=m;i++){
		scanf("%d %d %d %d",&t[i].u,&t[i].v,&t[i].w,&t[i].col);
		t[i].u++; t[i].v++;
	}
	cout<<wqs()<<endl;
	return 0;
}