P4047 [JSOI2010]部落划分

这篇博客介绍了一种使用二分搜索解决JSOI2010竞赛中的部落划分问题的方法。作者通过建立并查集来判断给定距离是否能将n个点划分为k个部落,并在二分过程中优化了判定条件。最终,通过二分搜索找到满足条件的最小距离,并以平方根形式输出结果。
摘要由CSDN通过智能技术生成

P4047 [JSOI2010]部落划分 (实数二分避免精度问题,最后开方)


想冲下100绿

这题说是MST,我看了之后直接想到了二分。

看了题解也不会MST

B U T BUT BUT 我好像知道有的时候我二分答案不会思考的问题了。

已经最近两个部落的距离是 mid ,判断能否满足 n 个点划分成 k 份?

首先:有两段性,可以二分答案。

我的想法是,有k份,每份都有啥呢?然后想到了并查集,有k个代表元素。从 n 个元素中选 k 个,然后再让其他点和这k个点比较,和哪个近就选哪个。

真是天才一般的想法 这复杂度没谁了。然后我就不会思考了。


看题解,人家都是这样思考的。从无到有 ,一开始啥都没有,如果两点距离小于mid就合并,最后看并查集代表元素和k的关系。

我思考的有问题的地方在于,我的判定没有直接用 mid!

然后我TM二分返回值也搞错了

当 cnt (并查集fa[i]==i) 的数量 小于 k 时,说明距离搞的太大了,否则说明距离太小了。

所以 : {cnt>=k, l = mid;} else r = mid;

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#include <set>
#include <map>
#include <vector>
#include <sstream>
#define pb push_back 
#define in insert
#define mem(f, x) memset(f,x,sizeof(f)) 
#define fo(i,a,n) for(int i=(a);i<=(n);++i)
#define fo_(i,a,n) for(int i=(a);i<(n);++i)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'
using namespace std;

template<typename T>
ostream& operator<<(ostream& os,const vector<T>&v){for(int i=0,j=0;i<v.size();i++,j++)if(j>=5){j=0;puts("");}else os<<v[i]<<" ";return os;}
template<typename T>
ostream& operator<<(ostream& os,const set<T>&v){for(auto c:v)os<<c<<" ";return os;}

typedef pair<int,int>PII;
typedef pair<long,long>PLL;

typedef long long ll;
typedef unsigned long long ull; 
const int N=1e5+10,M=1e5+10;
int n,k;
int fa[N];
int find(int x){
	return x == fa[x] ? x : fa[x] = find(fa[x]);
}
struct Node{
	int l,r;
}node[1100];

double dist(Node x,Node y){
	return (x.l-y.l)*(x.l-y.l)+(x.r-y.r)*(x.r-y.r);
}

bool ck(double mid){
	fo(i,1,n)fa[i]=i;
	int cnt = 0;
	fo(i,1,n){
		fo(j,i+1,n){
			if(dist(node[i],node[j])<=mid)
				fa[find(j)]=find(i);
		}
	}
	fo(i,1,n)if(fa[i]==i)cnt++;
	return cnt>=k;
}

void solve(){
	cin>>n>>k;
	fo(i,1,n){
		cin>>node[i].l>>node[i].r;
	}
	double l = 0 , r = 1e9;
	while(r-l>1e-4){
		double mid = (l+r)/2;
		// debug(mid);
		if(ck(mid)){
			l = mid;
		}
		else {
			r = mid;
		}
	}
	printf("%.2lf",sqrt(l));
}

int main()
{
	solve();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值