BZOJ2280 [Poi2011]Plot 二分+倍增+最小圆覆盖
题目传送门
https://lydsy.com/JudgeOnline/problem.php?id=2280
题解
显然对于一段的 \(q_i\),就是这一段的最小圆覆盖的圆心。
考虑二分一个 \(mid\),表示每一段的最小圆覆盖的半径的最大值。
然后我们可以在点序列上从开头一直往后走,直到半径大于 \(mid\),就算上一段并重新开始记录最小圆覆盖,然后把总的段数与 \(m\) 比较。
看上去没什么问题啊,时间复杂度 \(O(n\log eps^{-1})\),但是怎么 BZOJ 上时间限制有 300s,比紫荆花之恋还长啊。
等等,好像最小圆覆盖的时间复杂度的正确性需要把整个序列都打乱啊。不好,这样就不能保证了题目中要的连续的一段了。
那么我们想一想怎么更正这个算法。
每次从当前的起点 \(i\) 向右扩展的时候,可以二分一个 \(mid\),然后把 \(i..mid\) 这一段求一下最小圆覆盖?不对不对,这样二分的话,时间复杂度是 \(O(nm\log n\log eps^{-1})\)。
但是我们想要的是,对于长度为 \(l\) 的一段,能够通过 \(l\log l\) 把它二分出来,而不是 \(n\log n\)。
所以这里有一个小 trick,可以一个一个测试 \(i\) 右边 \(1, 2, 4, 8, \cdots\) 位之间的每一段满不满足条件。这样倍增下去,最后我们就可以得到一个信息 \(2^k-1 \leq l < 2^k-1\)。然后我们就可以在这个范围内二分了。这样因为每一次二分的 \(mid\) 都是和 \(l\) 在同一级别的,所以复杂度为 \(O(l\log l)\)。
这样就可以在 \(O(n\log l\log eps^{-1})\) 的时间复杂度内 AC 了。
下面是代码。如上文所属,这个代码的时间复杂度为 \(O(n\log l \log eps^{-1})\)。
#include<bits/stdc++.h>
#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back
template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b , 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b , 1 : 0;}
typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;
template<typename I>
inline void read(I &x) {
int f = 0, c;
while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
x = c & 15;
while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
f ? x = -x : 0;
}
const int N = 100000 + 7;
const double eps = 1e-8;
int n, m, cnt;
inline int dcmp(const double &x) { return fabs(x) <= eps ? 0 : (x < 0 ? -1 : 1); }
struct Point {
double x, y;
inline Point(const double &x = 0, const double &y = 0) : x(x), y(y) {}
} a[N], b[N], ans[N];
inline double dist(const Point &a, const Point &b) { return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); }
inline Point get_C(const Point &p1, const Point &p2, const Point &p3) {
double c1 = (p1.x * p1.x + p1.y * p1.y) - (p2.x * p2.x + p2.y * p2.y), a1 = 2 * (p1.x - p2.x), b1 = 2 * (p1.y - p2.y);
double c2 = (p2.x * p2.x + p2.y * p2.y) - (p3.x * p3.x + p3.y * p3.y), a2 = 2 * (p2.x - p3.x), b2 = 2 * (p2.y - p3.y);
double x = (b1 * c2 - b2 * c1) / (a2 * b1 - a1 * b2), y = (a1 * c2 - a2 * c1) / (a1 * b2 - a2 * b1);
return Point(x, y);
}
inline std::pair<Point, double> calc(int L, int R) {
int n = 0;
for (int i = L; i <= R; ++i) b[++n] = a[i];
std::random_shuffle(b + 1, b + n + 1);
Point O = b[1];
double r = 0;
for (int i = 2; i <= n; ++i) if (dcmp(dist(O, b[i]) - r) > 0) {
O = b[i], r = 0;
for (int j = 1; j < i; ++j) if (dcmp(dist(O, b[j]) - r) > 0) {
O = Point((b[i].x + b[j].x) / 2, (b[i].y + b[j].y) / 2), r = dist(b[i], O);
for (int k = 1; k < j; ++k) if (dcmp(dist(O, b[k]) - r) > 0) {
O = get_C(b[i], b[j], b[k]);
r = dist(b[i], O);
}
}
}
return std::make_pair(O, r);
}
inline bool check(const double &mid) {
cnt = 0;
int now = 1;
while (now <= n) {
int i = 1;
for (; now + (1 << i) - 1 <= n; ++i)
if (calc(now, now + (1 << i) - 1).se - mid > eps) break;
int l = now + (1 << (i - 1)) - 1, r = std::min(now + (1 << i) - 2, n);
while (l < r) {
int mid2 = (l + r + 1) >> 1;
if (calc(now, mid2).se - mid < eps) l = mid2;
else r = mid2 - 1;
}
ans[++cnt] = calc(now, l).fi, now = l + 1;
}
return cnt <= m;
}
inline void work() {
double l = 0, r = 2000000;
while (r - l >= eps) {
double mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid;
}
printf("%.8lf\n", r);
check(r);
printf("%d\n", cnt);
for (int i = 1; i <= cnt; ++i) printf("%.8lf %.8lf\n", ans[i].x, ans[i].y);
}
inline void init() {
srand(time(0) + (ull)new char);
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) scanf("%lf%lf", &a[i].x, &a[i].y);
}
int main() {
#ifdef hzhkk
freopen("hkk.in", "r", stdin);
#endif
init();
work();
fclose(stdin), fclose(stdout);
return 0;
}
BZOJ2280 [Poi2011]Plot 二分+倍增+最小圆覆盖的更多相关文章
- 【bzoj2280】[Poi2011]Plot 二分+倍增+二分+最小圆覆盖
题目描述 给出一系列点p_1, p_2, ... , p_n,将其分成不多余m个连续的段,第i段内求一个点q_i,使得q_i到这段内点的距离的最大值的最大值最小 输入 第一行,n m下面n行,每行两个 ...
- BZOJ2280 [Poi2011]Plot
恩..这题真是sxbk 我们先二分答案,然后判断答案是否满足要求 判断方法是二分当前段的长度一直做到底,当然我们可以用倍增这样快一点,直接随机增量就可以了 然后就是卡常..... 然后就是卡精度QAQ ...
- 【做题】POI2011R1 - Plot——最小圆覆盖&倍增
原文链接 https://www.cnblogs.com/cly-none/p/loj2159.html 题意:给出\(n\)个点,你需要按编号将其划分成不超过\(m\)段连续的区间,使得所有每个区间 ...
- 洛谷P4586 [FJOI2015]最小覆盖双圆问题(最小圆覆盖)
题面 传送门 前置芝士 最小圆覆盖 题解 我们按照\(x\)坐标排序,然后二分中间点,把点分成左右两边,对两边都做一个最小圆覆盖,那么半径大一点的那个就是答案了.然后对半径大的那一边继续二分就行了 然 ...
- 【BZOJ-1336&1337】Alie最小圆覆盖 最小圆覆盖(随机增量法)
1336: [Balkan2002]Alien最小圆覆盖 Time Limit: 1 Sec Memory Limit: 162 MBSec Special JudgeSubmit: 1573 ...
- Bzoj 1336&1337 Alien最小圆覆盖
1336: [Balkan2002]Alien最小圆覆盖 Time Limit: 1 Sec Memory Limit: 162 MBSec Special Judge Submit: 1473 ...
- hdu3007Buried memory(最小圆覆盖)
链接 普通的暴力复杂度达到O(n^4),对于这题肯定是不行的. 解法:随机增量算法 参考http://www.2cto.com/kf/201208/149602.html algorithm:A.令C ...
- [BZOJ 3564] [SHOI2014] 信号增幅仪 【最小圆覆盖】
题目链接:BZOJ - 3564 题目分析 求最小椭圆覆盖,题目给定了椭圆的长轴与 x 轴正方向的夹角,给定了椭圆长轴与短轴的比值. 那么先将所有点旋转一个角度,使椭圆长轴与 x 轴平行,再将所有点的 ...
- [BZOJ 1336] [Balkan2002] Alien最小圆覆盖 【随机增量法】
题目链接:BZOJ - 1336 题目分析 最小圆覆盖有一个算法叫做随机增量法,看起来复杂度像是 O(n^3) ,但是可以证明其实平均是 O(n) 的,至于为什么我不知道= = 为什么是随机呢?因为算 ...
随机推荐
- c++读取数据
1.cin (1)输入数字 遇到空格.回车.tab结束返回引用. #include <iostream> using namespace std; main () { int a,b; c ...
- [CSP-S模拟测试]:折纸(模拟)
题目描述 小$s$很喜欢折纸.有一天,他得到了一条很长的纸带,他把它从左向右均匀划分为$N$个单位长度,并且在每份的边界处分别标上数字$0\sim n$.然后小$s$开始无聊的折纸,每次他都会选择一个 ...
- Spring JDBCTemplate 简单使用
Spring JDBCTemplate applicationContext.xml配置 <?xml version="1.0" encoding="UTF-8&q ...
- php面试专题---8、会话控制考点
php面试专题---8.会话控制考点 一.总结 一句话总结: 主要是cookie和session的区别,以及用户禁用cookie之后怎么使用session 1.为什么要使用会话控制技术? 因为http ...
- springboot参数校验
为了能够进行嵌套验证,必须手动在Item实体的props字段上明确指出这个字段里面的实体也要进行验证.由于@Validated不能用在成员属性(字段)上,但是@Valid能加在成员属性(字段)上,而且 ...
- python 按二维数组的某行或列排序 (numpy lexsort)
lexsort支持对数组按指定行或列的顺序排序:是间接排序,lexsort不修改原数组,返回索引. (对应lexsort 一维数组的是argsort a.argsort()这么使用就可以:argsor ...
- 关于在eclipse中配置tomcat的各种坑
先说在windows下的,java环境什么的就不再记录了,记住装java ee之前,先要装好java se这样java ee才能顺利安装. 主要是安装好tomcat之后,在eclipse中进行配置的时 ...
- Ajax表单提交给C#后台选中的checkbox值
HTML页面: <input name="payWay" type="checkbox" value="1" /> <i ...
- 微信小程序列表时间戳转换
第一步先写js 随便命名为times.js function toDate(number){ var n=number * 1000; var date = new Date(n) ...
- Day3---Python的time库的一些简单函数以及用法
time库的一些函数 time.time () : 获取当前时间戳,即计算机内部时间值,浮点数 >>>import time >>> time.time() 1 ...