P3517 [POI 2011] WYK-Plot

Sol

很神秘的 trick。

注意到最小圆覆盖是 \(O(len)\) 的,那么如果直接二分每个段,肯定会 TLE。

于是考虑先倍增找出一个和长度同级别的一个范围,然后在这个范围里面二分,每段的次数就是 \(O(len \log len)\),那么全部加起来就是 \(O(n \log n)\)check

对于 \(O(len)\) 复杂度的 check 可以用这个 trick。

Code

#include <bits/stdc++.h>
#define x first
#define y second
#define pb push_back
#define pf push_front
#define desktop "C:\\Users\\incra\\Desktop\\"
#define IOS ios :: sync_with_stdio (false),cin.tie (0),cout.tie (0)
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair <int,int> PII;
const int dx[] = {1,0,-1,0},dy[] = {0,-1,0,1};
template <typename T1,typename T2> bool tomax (T1 &x,T2 y) {
	if (y > x) return x = y,true;
	return false;
}
template <typename T1,typename T2> bool tomin (T1 &x,T2 y) {
	if (y < x) return x = y,true;
	return false;
}
LL power (LL a,LL b,LL p) {
	LL ans = 1;
	while (b) {
		if (b & 1) ans = ans * a % p;
		a = a * a % p;
		b >>= 1;
	}
	return ans;
}
int fastio = (IOS,0);
#define endl '\n'
#define puts(s) cout << (s) << endl
LL _time_ = chrono :: steady_clock :: now ().time_since_epoch ().count ();
mt19937_64 rng (_time_);
const int N = 100010;
const double EPS = 1e-9,PI = acos (-1);
int n,m;
struct point {
	double x,y;
	point (double _x = 0,double _y = 0) {x = _x,y = _y;}
}a[N],b[N];
vector <PII> ans;
point operator + (point a,point b) {return point (a.x + b.x,a.y + b.y);}
point operator - (point a,point b) {return point (a.x - b.x,a.y - b.y);}
point operator * (point a,double b) {return point (a.x * b,a.y * b);}
point operator / (point a,double b) {return point (a.x / b,a.y / b);}
struct circle {
	point p;
	double r;
	circle (point _p,double _r) {p = _p,r = _r;}
};
double get_dis (point a,point b) {
	double dx = a.x - b.x,dy = a.y - b.y;
	return sqrtl (dx * dx + dy * dy);
}
double sq (double x) {return x * x;}
circle get (point a,point b,point c) {
	double a1 = b.x - a.x,a2 = c.x - a.x,b1 = b.y - a.y,b2 = c.y - a.y;
	double c1 = sq (b.x) - sq (a.x) + sq (b.y) - sq (a.y);
	double c2 = sq (c.x) - sq (a.x) + sq (c.y) - sq (a.y);
	point p = point ((b2 * c1 - b1 * c2) / (b2 * a1 * 2 - b1 * a2 * 2),(a2 * c1 - a1 * c2) / (a2 * b1 * 2 - a1 * b2 * 2));
	return circle (p,get_dis (p,a));
}
circle solve (int l,int r) {
	for (int i = l;i <= r;i++) b[i] = a[i];
	shuffle (b + l,b + r + 1,rng);
	circle ans (b[l],0);
	for (int i = l;i <= r;i++) {
		if (get_dis (ans.p,b[i]) > ans.r + EPS) {
			ans = circle (b[i],0);
			for (int j = l;j <= i - 1;j++) {
				if (get_dis (ans.p,b[j]) > ans.r + EPS) {
					ans = circle ((b[i] + b[j]) / 2,get_dis (b[i],b[j]) / 2);
					for (int k = l;k <= j - 1;k++) {
						if (get_dis (ans.p,b[k]) > ans.r + EPS) ans = get (b[i],b[j],b[k]);
					}
				}
			}
		}
	}
	return ans;
}
bool check (double x) {
	ans.clear ();
	int cnt = 0;
	for (int i = 1;i <= n;) {
		int k = 1;
		for (;i + (1 << k) - 1 <= n;k++) {
			if (solve (i,i + (1 << k) - 1).r > x) break;
		}
		int l = i + (1 << k - 1) - 1,r = min (i + (1 << k) - 1,n);
		while (l < r) {
			int mid = l + r + 1 >> 1;
			if (solve (i,mid).r < x + EPS) l = mid;
			else r = mid - 1;
		}
		cnt++;
		if (cnt > m) return 0;
		ans.pb ({i,l});
		i = l + 1;
	}
	return 1;
}
void mian () {
	cin >> n >> m;
	for (int i = 1;i <= n;i++) cin >> a[i].x >> a[i].y;
	double l = 0,r = 1e9;
	for (int _ = 1;_ <= 50 && r - l > EPS;_++) {
		double mid = (l + r) / 2;
		if (check (mid)) r = mid;
		else l = mid;
	}
	check (r);
	cout << fixed << setprecision (8) << r << endl;
	cout << ans.size () << endl;
	for (auto [l,r] : ans) {
		auto res = solve (l,r);
		cout << fixed << setprecision (8) << res.p.x << ' ' << res.p.y << endl;
	}
}
int main () {
	int T = 1;
	// cin >> T;
	while (T--) mian ();
	return 0;
}
posted @ 2025-05-01 13:34  incra  阅读(25)  评论(0)    收藏  举报