【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;
}
时间: 2024-10-13 05:26:34

【bzoj2280】[Poi2011]Plot 二分+倍增+二分+最小圆覆盖的相关文章

BZOJ2280 [Poi2011]Plot

恩..这题真是sxbk 我们先二分答案,然后判断答案是否满足要求 判断方法是二分当前段的长度一直做到底,当然我们可以用倍增这样快一点,直接随机增量就可以了 然后就是卡常..... 然后就是卡进度QAQQQQQQQ没了 1 /************************************************************** 2 Problem: 2280 3 User: rausen 4 Language: C++ 5 Result: Accepted 6 Time:90

【做题】POI2011R1 - Plot——最小圆覆盖&amp;倍增

原文链接 https://www.cnblogs.com/cly-none/p/loj2159.html 题意:给出\(n\)个点,你需要按编号将其划分成不超过\(m\)段连续的区间,使得所有每个区间的最小圆覆盖的半径的最大值最小.输出这个最小化的最大值和方案(圆心). \(n,m \leq 10^5\) 显然要二分答案.然后,一个区间就是越长越好. 首先,考虑最小圆覆盖\(O(n)\)的随机增量法,但为了保证复杂度,我们需要能够random_shuffle.如果直接按照编号顺序添加点,时间复杂

题解——dinner(二分+倍增)

题解--dinner(二分+倍增) 见ssw02的:题解--慕斯蛋糕 好像这道题 二分+二分+贪心 也可以.(日后更,T3还没改) 如有不足,请大佬指出 原文地址:https://www.cnblogs.com/ssw02/p/11394142.html

POJ 3685 二分套二分

Given a N × N matrix A, whose element in the i-th row and j-th column Aij is an number that equals i2 + 100000 × i + j2 - 100000 × j + i × j, you are to find the M-th smallest element in the matrix. Input The first line of input is the number of test

LibreOJ #2006. 「SCOI2015」小凸玩矩阵 二分答案+二分匹配

#2006. 「SCOI2015」小凸玩矩阵 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: 匿名 提交提交记录统计讨论测试数据 题目描述 小凸和小方是好朋友,小方给小凸一个 N×M N \times MN×M(N≤M N \leq MN≤M)的矩阵 A AA,要求小凸从其中选出 N NN 个数,其中任意两个数字不能在同一行或同一列,现小凸想知道选出来的 N NN 个数中第 K KK 大的数字的最小值是多少. 输入格式 第一行给出三个整数

ZOJ 3156 Taxi (二分匹配+二分查找)

题目链接:Taxi Taxi Time Limit: 1 Second      Memory Limit: 32768 KB As we all know, it often rains suddenly in Hangzhou during summer time.I suffered a heavy rain when I was walking on the street yesterday, so I decided to take a taxi back school. I foun

hdu1151Air Raid (二分匹配,最小路径覆盖)

Problem Description Consider a town where all the streets are one-way and each street leads from one intersection to another. It is also known that starting from an intersection and walking through town's streets you can never reach the same intersec

POJ-3579 Median---二分第k大(二分套二分)

题目链接: https://cn.vjudge.net/problem/POJ-3579 题目大意: 求的是一列数所有相互之间差值的序列的最中间的值是多少. 解题思路: 可以用二分套二分的方法求解第m大,和POJ-3685类似,这里的模板也差不多 枚举第m大x,判断小于等于x的数目是不是大于m,如果大于m说明x >= 第m大,调整区间r = mid - 1 不然l = mid + 1 此处是大于m而不是小于m是因为一个数可能出现多次,那么小于等于中间的数目可能就比m大 在计算小于等于x的数目的时

浅谈二分和二分答案

一般来讲我们会在以下情况用到二分: 求单调函数的零点 求一堆东西的最小值最大是多少 很难直接算出答案,但是很好判定答案合不合法 如果想学就继续看吧! 二分查找 二分是一种可以再\(\mathcal{O}(\mathrm{ch}\log m)\)(\(m\)为数据规模,\(\mathrm{ch}\)为判断状态合法性check()函数时间复杂度)时间复杂度内求解问题的方法,主要是一个分治,每次将搜索范围减半. 我们以二分查找引入. 有一个有序序列,二分查找的思想如下: 1.定义L,R,mid代表答案