bzoj2280[Poi2011]Plot 二分+倍增+二分+最小圆覆盖
Posted GXZlegend
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj2280[Poi2011]Plot 二分+倍增+二分+最小圆覆盖相关的知识,希望对你有一定的参考价值。
题目描述
给出一系列点p_1, p_2, ... , p_n,将其分成不多余m个连续的段,第i段内求一个点q_i,使得q_i到这段内点的距离的最大值的最大值最小
输入
第一行,n m
下面n行,每行两个整数,表示p_i的x y坐标
1<=m<=n<=100000
坐标范围[-1000000,1000000] 0<p,q,r<=150,输入中至少包含一个’N’
输出
第一行,q_i到这段内点的距离的最大值的最大值的最小值
第二行,分成的段数k
下面k行,每行两个实数,表示q_k的x y坐标
All the real numbers should be printed with at most 15 digits after the decimal point.
样例输入
7 2
2 0
0 4
4 4
4 2
8 2
11 3
14 2
样例输出
3.00000000
2
2.00000000 1.76393202
11.00000000 1.99998199
题解
二分+倍增+二分+最小圆覆盖
最大值最小,一眼二分答案。
考虑怎么判定:取前任意个点跑最小圆覆盖,直到半径大于mid为止,最后看分成段的数目是否超过m即可。
然而这样暴力跑是不行的,需要优化这个过程;同时直接再二分也是不可取的,因为二分的区间长度是满的,最坏情况下每段长度都为1,每段都需要$O(n\\log n)$的时间来找,GG。
所以需要让求最小圆覆盖的点数只与每段长有关。考虑倍增,处理出第一个$j$,满足$2^j$个不满足条件。然后在$2^{j-1}$到$2^j$之间二分处理即可。这样第一部分时间复杂度为$O(len)$,第二部分时间复杂度为$O(len\\log len)$,与n无关,所以可以满足时间要求。
因此总的时间复杂度为$O(n\\log^2n)$,然而常数巨大。。。所以才给300s。。。
最小圆覆盖的求法参见 http://www.cnblogs.com/GXZlegend/p/7467029.html
CQzhangyu说卡精度,然而没看出来= = 1A了
#include <cstdio> #include <algorithm> #define N 100010 #define eps 1e-15 using namespace std; int n , m , id[N] , tot; double mid , x[N] , y[N] , ansx[N] , ansy[N]; inline double squ(double x) { return x * x; } bool check(int lp , int rp , double &vx , double &vy) { int i , j , k , len = rp - lp + 1; double px = 0 , py = 0 , r = 0 , x1 , x2 , x3 , y1 , y2 , y3; for(i = lp ; i <= rp ; i ++ ) id[i - lp + 1] = i; random_shuffle(id + 1 , id + len + 1); for(i = 1 ; i <= len ; i ++ ) { if(squ(px - x[id[i]]) + squ(py - y[id[i]]) > r + eps) { px = x[id[i]] , py = y[id[i]] , r = 0; for(j = 1 ; j < i ; j ++ ) { if(squ(px - x[id[j]]) + squ(py - y[id[j]]) > r + eps) { px = (x[id[i]] + x[id[j]]) / 2 , py = (y[id[i]] + y[id[j]]) / 2 , r = (squ(x[id[i]] - x[id[j]]) + squ(y[id[i]] - y[id[j]])) / 4; if(r > mid * mid + eps) return 0; for(k = 1 ; k < j ; k ++ ) { if(squ(px - x[id[k]]) + squ(py - y[id[k]]) > r + eps) { x1 = x[id[i]] , x2 = x[id[j]] , x3 = x[id[k]]; y1 = y[id[i]] , y2 = y[id[j]] , y3 = y[id[k]]; px = (x1 * x1 * (y2 - y3) + x2 * x2 * (y3 - y1) + x3 * x3 * (y1 - y2) - (y1 - y2) * (y2 - y3) * (y3 - y1)) / (x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) / 2; py = ((x1 - x2) * (x2 - x3) * (x3 - x1) - y1 * y1 * (x2 - x3) - y2 * y2 * (x3 - x1) - y3 * y3 * (x1 - x2)) / (x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) / 2; r = squ(px - x1) + squ(py - y1); if(r > mid * mid + eps) return 0; } } } } } } vx = px , vy = py; return 1; } bool judge() { int i , len , last , l , r , mid; bool flag; double tmpx , tmpy; tot = 0; for(i = 1 ; i <= n ; i = last + 1) { if(tot == m) return 0; flag = 0; for(len = 1 ; !flag ; len <<= 1) { if(!check(i , min(i + len - 1 , n) , tmpx , tmpy)) { flag = 1; break; } else if(i + len - 1 >= n) { tot ++ , ansx[tot] = tmpx , ansy[tot] = tmpy; return 1; } l = i + len; } r = min(i + len - 1 , n) , last = l - 1; while(l <= r) { mid = (l + r) >> 1; if(check(i , mid , tmpx , tmpy)) last = mid , l = mid + 1; else r = mid - 1; } check(i , last , tmpx , tmpy) , tot ++ , ansx[tot] = tmpx , ansy[tot] = tmpy; } return 1; } int main() { srand(20011011); int i , cnt = 50; double l = 0 , r = 2e6; scanf("%d%d" , &n , &m); for(i = 1 ; i <= n ; i ++ ) scanf("%lf%lf" , &x[i] , &y[i]); while(cnt -- ) { mid = (l + r) / 2; if(judge()) r = mid; else l = mid; } printf("%.15lf\\n" , r); mid = r , judge(); printf("%d\\n" , tot); for(i = 1 ; i <= tot ; i ++ ) printf("%.15lf %.15lf\\n" , ansx[i] , ansy[i]); return 0; }
以上是关于bzoj2280[Poi2011]Plot 二分+倍增+二分+最小圆覆盖的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ2525[Poi2011]Dynamite 二分+树形DP