[cf 1190] E

题意

平面上有(n)个点,你可以画(m)条直线,使得每个点和原点的交线和(m)条直线至少有一个相交点。问离原点最近的直线于原点距离的最大值。

题解

显然应该先二分最近直线离原点距离。
可以把这个距离看成半径,画一个圆。
如果我们把点从2D平面上映射到一个序列上(或者说一个环状序列),我们可以把辐角作为偏序。
那么这样,如果用一条线能把两个点((i, j))都保护住,在圆上的表现就是两个点和两个点在圆上的切点的关系,表现到序列上即

[arg_i leq arg_{tg_j} leq arg_{tg_i} leq arg_j ]

其中(tg_i)(tg_j)是满足条件的(i)(j)的某个关于圆的切点(一般一个点关于圆的切点有两个)。
并且这只是一种合法的情况。(i)(j)位置互换也是可以的。
所以,我们就得到了一个思路:过每个点作圆的切线,设切点的俯角分别为(arg_{l_i})(arg_{r_i}),那么这个点就可以映射到一个环状序列上的一段区间(arg_{l_i})(arg_{r_i})
现在的问题是,我们要取出几个的区间,要求覆盖整个环状序列,并且区间数最少(其实区间数最少就可以等价于所需直线最少)
这个问题对于环状序列和线性序列本质没有区别,破环为链再倍长即可解决这个问题。考虑序列上如何解决。
(f_{i, j})为在第(i)个位置开始,向后取了(2 ^ j)个区间,铺满的极大区间为([i, f_{i, j}])(不考虑(i)之前的位置)。
然后可以(mathcal O(n))预处理出(f_{i, 0}),然后再倍增可以得到(f_{i, j})
对于一个序列,只要判断从首位开始,取了(m)个区间,是否能铺满([1, L])
对于一个环状序列,只要枚举从哪一个位置开始,做一次序列上的判断即可。
复杂度(mathcal O(n log n log V))

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 2e5 + 10, H = 20;
const db pi = acos(-1.0);
int n, m, L, f[H][N];
struct P {
	int x, y;
	db arg, dis;
} a[N];
struct seg {
	db l, r;
	bool operator < (const seg &o) const {
		return r == o.r ? l < o.l : r < o.r;
	}
} s[N];
bool check (db r) {
	for (int i = 1; i <= n; ++i) {
		if (r > a[i].dis) {
			return 0;
		}
		db dlta = acos(r / a[i].dis);
		s[i] = (seg) {a[i].arg - dlta, a[i].arg + dlta};
		if (s[i].r > pi) {
			s[i].l -= pi * 2;
			s[i].r -= pi * 2;
		}
	}
	sort(s + 1, s + n + 1);
	for (int i = 1; i <= n; ++i) {
		s[i + n] = (seg) {s[i].l + pi * 2, s[i].r + pi * 2};
	}
	f[0][L + 1] = L + 1;
	for (int i = 1, j = 0; i <= L; f[0][i] = j, ++i) {
		for (j = (j <= i ? i + 1 : j); j <= L && s[j].l <= s[i].r; ++j);
	}
	for (int i = 1; i < H; ++i) {
		for (int j = 1; j <= L + 1; ++j) {
			f[i][j] = f[i - 1][f[i - 1][j]];
		}
	}
	for (int i = 1, j; i <= n; i++) {
		j = i;
		for (int k = H - 1; ~k; --k) {
			if (m >> k & 1) {
				j = f[k][j];
			}
		}
		if (j >= i + n) {
			return 1;
		}
	}
	return 0;
}
int main () {
	scanf("%d%d", &n, &m), L = n << 1;
	for (int i = 1; i <= n; ++i) {
		scanf("%d%d", &a[i].x, &a[i].y);
		a[i].arg = atan2(a[i].y, a[i].x);
		a[i].dis = sqrtl(1ll * a[i].x * a[i].x + 1ll * a[i].y * a[i].y);
	}
	db l = 0, r = a[1].dis, mid;
	while (r - l >= 1e-10) {
		mid = (l + r) / 2;
		check(mid) ? l = mid : r = mid;
	}
	printf("%.11lf
", l);
	return 0;
}
原文地址:https://www.cnblogs.com/psimonw/p/11276983.html