• [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;
    }
    
  • 相关阅读:
    能粘贴Word 内容(含公式)的HTML编辑器
    能粘贴Word 内容(含公式)的Web编辑器
    开心的小明(背包)
    苹果(背包)
    单调递增最长子序列
    卡特兰数 Catalan数 ( ACM 数论 组合 )
    SAP CRM订单状态管理的一些重要的数据库表
    SAP WebClient UI的会话重启原理
    自己开发的一个SAP CRM订单统计工具
    SAP CRM订单抬头级别的组织架构数据是如何自动拷贝到行项目的
  • 原文地址:https://www.cnblogs.com/psimonw/p/11276983.html
Copyright © 2020-2023  润新知