bzoj2654:tree

传送门

wqs二分果题
就是切线对应多个点有点烦,别的很好想
当切线对应多个点时,就取横坐标最小的决策点,最后取横坐标最大的决策点求答案。
代码:

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
#define rg register
void read(int &x){
	char ch;bool ok=0;
	for(ch=getchar();!isdigit(ch);ch=getchar())if(ch=='-')ok=1;
	for(x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
	if(ok)x=-x;
}
const int maxn=1e5+10;
int f[maxn],n,m,k,ans,num;
struct oo{int x,y,z,id;}a[maxn],g[maxn];
bool cmp(oo a,oo b){return a.z==b.z?a.id>b.id:a.z<b.z;}
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
bool check(int now){
	for(rg int i=1;i<=n;i++)f[i]=i;
	int tot=0,sum=0;num=0;
	for(rg int i=1;i<=m;i++)g[++tot]=a[i],g[tot].z=!a[i].id?g[tot].z-now:g[tot].z;
	sort(g+1,g+m+1,cmp);
	for(rg int i=1;i<=m;i++)
		if(find(g[i].x)!=find(g[i].y)){
			sum+=(g[i].id==0),num+=g[i].z;
			f[find(g[i].x)]=find(g[i].y);
		}
	return sum<=k;
}
int main(){
	read(n),read(m),read(k);
	for(rg int i=1,x,y,z,op;i<=m;i++){
		read(x),read(y),read(z),read(op);x++,y++;
		a[i]=(oo){x,y,z,op};
	}
	int l=-105,r=105;
	while(l<=r){
		int mid=(l+r)>>1;
		if(check(mid))l=mid+1,ans=num+mid*k;
		else r=mid-1;
	}
	printf("%d
",ans);
}

原文地址:https://www.cnblogs.com/lcxer/p/11143242.html