【国家集训队】Tree I

【国家集训队】Tree I

Description

给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有 \(need\) 条白色边的生成树。

题目保证有解。

Input

第一行 \(V,E,need\) 分别表示点数,边数和需要的白色边数。

接下来 \(E\) 行,每行 \(s,t,c,col\) 表示这边的端点(点从 \(0\) 开始标号),边权,颜色(\(0\) 白色 \(1\) 黑色)。

Output

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

Sample Input

2 2 1
0 1 1 1
0 1 2 0

Sample Output

2

Data Constraint

对于 \(5\%\) 的数据,\(V\leq 10\)

对于另 \(15\%\) 的数据,\(V\leq 15\)

对于 \(100\%\) 的数据,\(V\leq 5\times10^4,E\leq 10^5\)

所有数据边权为 \([1,100]\) 中的正整数。

Solution

这个是wqs的板子,所以不讲做法了

由于是第一次写,发现了很多问题,写个题解总结一下

涉及排序一定要想清楚它跟凸包的关系

比如这题中一定要钦定权值相同先选哪个颜色

因为一条直线会截得一整段,只有钦定才能得知选的是最靠右还是最靠左

二分写法问题

这题甚至使我改变了以往的二分写法。。。

采用\(l=mid-1,r=mid+1,lst=mid\)的写法

一般来说,不需要二分小数

因为分界点之间的纵坐标的差一般是整数

答案是加上\(m*k\)\(m\)为物品个数,\(k\)为斜率)

而不是任意选中我们实际选择的物品数量

这是因为直线切的是包含\(m\)的一整段

Code

#include<bits/stdc++.h>
using namespace std;
#define F(i,a,b) for(int i=a;i<=b;i++)
#define Fd(i,a,b) for(int i=a;i>=b;i--)
#define N 100010

int v,e,n,ans,f[N];
struct node{
	int st,en,v,col;
	bool operator<(const node&x)const{return v<x.v||v==x.v&&col<x.col;}
};
vector<node>edge,a;

int get(int x){return f[x]==x?x:(f[x]=get(f[x]));}

int check(int x){
	int res=0,sum=0;
	a.clear();
	for(auto d:edge){
		if(!d.col)a.push_back((node){d.st,d.en,d.v-x,d.col});
		else a.push_back(d);
	}
	sort(a.begin(),a.end());
	F(i,0,v-1)f[i]=i; 
	int cnt=0;
	for(auto d:a){
		int A=get(d.st),B=get(d.en);
		if(A==B)continue;cnt++;
		f[A]=B;
		sum+=d.v;
		if(!d.col)res++;
	}
	ans=sum;
	return res;
}

int main(){
	scanf("%d%d%d",&v,&e,&n);
	F(i,1,e){
		int A,B,C,D;
		scanf("%d%d%d%d",&A,&B,&C,&D);
		edge.push_back((node){A,B,C,D});
	}
	int l=-100,r=100,mid,lst;
	while(l<=r){ 
		mid=l+r>>1;
		int w=check(mid);
		if(w>=n)lst=mid,r=mid-1;
		else l=mid+1;
	}
	check(lst);
	printf("%d",ans+n*lst);
	return 0;
}
posted @ 2022-09-12 21:20  冰雾  阅读(41)  评论(0)    收藏  举报