JZOJ 6489【GDOI2020模拟02.29】守卫疆域guard (最小k覆盖圆,二分+扫描线)
Description:
\(n<=500\)
题解:
先想一个暴力的做法。
我们知道最小覆盖圆一定是原点集 中的三个点形成的圆或者是以原点集 中的两点为直径的圆。
同样的道理,最小k覆盖圆肯定也满足这个性质。
那么枚举这些圆,都判一下即可,这是\(O(n^4)\)的。
对于这题,可以先二分答案。
因为圆一定至少经过一个点,所以枚举这个点x,考虑圆心绕着它旋转了一圈。
对于每一个点y,当圆心和x的几角在一个区间时,y会在圆里。
利用余弦定理去计算这个几角区间,然后扫描线看有没有被覆盖不少于k次的区间即可。
复杂度:\(O(n^2~log~V~log~n)\)
Code:
#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define ff(i, x, y) for(int i = x, B = y; i < B; i ++)
#define fd(i, x, y) for(int i = x, B = y; i >= B; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;
#define db double
struct P {
db x, y;
P(){}
P(db _x, db _y) { x = _x, y = _y;}
};
const db eps = 1e-8;
P operator - (P a, P b) { return P(a.x - b.x, a.y - b.y);}
db len(P a) { return sqrt(a.x * a.x + a.y * a.y);}
db len2(P a) { return a.x * a.x + a.y * a.y;}
bool operator < (P a, P b) { return a.x < b.x;}
db ang(P a) { return atan2(a.y, a.x);}
const int N = 505;
const db pi = acos(-1);
int n, k;
P a[N], d[N * 2]; int d0;
int pd(db r) {
if(r < 0) return 0;
fo(i, 1, n) {
d0 = 0;
int cnt = 1;
fo(j, 1, n) if(i != j) {
if(len2(a[j] - a[i]) < eps) {
cnt ++;
} else
if(len(a[j] - a[i]) <= 2 * r) {
db w = ang(a[j] - a[i]);
db t = (pi - acos((1 - len2(a[j] - a[i]) / (2 * r * r)))) / 2;
db l = w - t, r = w + t;
if(l <= r) {
d[++ d0] = P(l, 1);
d[++ d0] = P(r, -1);
} else {
cnt ++;
d[++ d0] = P(r, -1);
d[++ d0] = P(l, 1);
}
}
}
sort(d + 1, d + d0 + 1);
fo(j, 1, d0) {
cnt += (int) d[j].y;
if(cnt >= k) return 1;
}
}
return 0;
}
int main() {
freopen("guard.in", "r", stdin);
freopen("guard.out", "w", stdout);
scanf("%d %d", &n, &k);
fo(i, 1, n) scanf("%lf %lf", &a[i].x, &a[i].y);
db ans = 1e4;
for(db p = 1e4; p > 1e-5; p /= 2)
if(pd(ans - p)) ans -= p;
pp("%.10lf\n", ans);
}
转载注意标注出处:
转自Cold_Chair的博客+原博客地址