【计算几何】【单调队列】【bitset】ball

【描述】
球球从来都不承认自己很圆,它只认为自己很胖。

球球为了证明这个世界上有东西比它更圆,所以它准备用圆规在一个二维平面上画若干个圆。现在球球 已经钦定了 n 个可以作为圆心的坐标,它会选择若干个点,并且将它们按照一定顺序连接后能够形成一 个凸多边形, 然后以这些点为圆心来画圆,同时使得这些圆相交部分的面积恰好是 0。

就在球球准备动手证明这个世界上有东西比它更圆的时候,它忽然发 现它的圆规是一次性的:一旦指定 了半径之后就无法再更改了,只能用来画半径固定的圆了。

现在球球很苦恼,但是为了证明这一切,它仍然想完成它的杰作。球球认为半径越大,圆就显得更加的 圆滚滚,因此它就想知道它能够画的最大的圆的半径是多少。

这个问题对于曾经拿过全国 rk1 的球球来说太 Simple,但是它正在努力将圆规升级,让圆规可以反复使 用。这样一来它就没有时间直接调用它自 己写的、能够得到任何问题答案的 Ball.GetAns() 函数得到答 案,所以需要你来告诉它结果了。

【输入】
第一行一个整数 n,含义见题面。(n<=4000)

接下来是 n 行,每行两个整数 x, y,代表球球钦定的坐标。

【输出】
一行一个实数,表示球球可以选择的最大半径。 答案误差在10−6以内都是可以被接受的。 时限(2s)可能比较紧,请注意常数优化。

【样例输入】
3
0 0
1 1
1 0
【样例输出】
0.50000000000

思路

首先,一个显然的结论,三角形一定是最优的。因为对于任意一个多边形,把它变成三角形一定不会使答案更差。
第一种做法是,枚举每一个点,然后将其它点相对于这个点的极角进行排序。对于每一个枚举的点,我们只考虑夹这个点的两条边是否为三角形中三边中最小值。换句话说,我们需要保证对边不是最小值。这里就需要用到一个简单的结论,任意三角形满足“大角对大边”。因此,如果我们保证更新答案时两条边的夹角大于六十度,那么对边就一定不是最小值。然后,对于剩下的两条边,我们可以利用单调队列维护最值。因此总时间复杂度为O( n 2 l o g n n^{2}log {n} n2logn)。但是听说这道题卡常卡到标程超时。
这道题做法很多,包括二分+旋转卡壳。其中有一种做法代码十分简单。考虑对于 n 2 2 \frac{n^{2}}{2} 2n2条边按大小进行排序。按边权从大到小依次把每条边加进来。显然,构成三角形时加入的那条边就是答案。问题在于怎么判断是否构成三角形。考虑加入边的两个顶点,如果它们已经同时与某个点直接相连,那么就会构成三角形。所以我们可以用bitset维护每个点和哪些点已经有边,判断时将bitset交起来判断是否有1即可。这种做法属于玄学复杂度,瓶颈在于会枚举多少条边才能得到最优解。最坏情况O( n 3 32 \frac{n^{3}}{32} 32n3),实际上远小于这个复杂度。

//标算做法
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define ll long long
#define MAX 10010
#define Sqr(x) ((x)*(x))
#define mp make_pair
#define fr first
#define sd second
const double Pi=acos(-1);
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int x[MAX],y[MAX],n,ans;
pair<double,int> a[MAX<<1];
int Q[MAX],L,R;
void add(int x){while(L<=R&&Q[R]<x)--R;Q[++R]=x;}
void del(int x){if(L<=R&&Q[L]==x)++L;}
int main()
{
	n=read();
	for(int i=1;i<=n;++i)x[i]=read(),y[i]=read();
	for(int i=1;i<=n;++i)
	{
		int k=0;L=1;R=0;
		for(int j=1;j<=n;++j)
			if(i^j)a[++k]=mp(atan2(y[j]-y[i],x[j]-x[i]),Sqr(x[j]-x[i])+Sqr(y[j]-y[i]));
		sort(&a[1],&a[k+1]);
		for(int j=1;j<=k;++j)a[j+k]=a[j],a[j+k].fr+=2*Pi;
		for(int j=1,l=1,r=1;j<=k;++j)
		{
			while(a[r].fr-a[j].fr<=Pi)add(a[r++].sd);
			while(a[l].fr-a[j].fr<=Pi/3)del(a[l++].sd);
			if(L<=R)ans=max(ans,min(a[j].sd,Q[L]));
		}
	}
	printf("%.10lf\n",sqrt(ans)/2);
	return 0;
}
//玄学做法
#include<bits/stdc++.h>
#define re register
using namespace std;
const int N=4e3+1;
inline int red()
{
    int data=0;int w=1; char ch=0;
    ch=getchar();
    while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
    if(ch=='-') w=-1,ch=getchar();
    while(ch>='0' && ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
    return data*w;
}
int n,m,u,v;
int x[N],y[N];
bitset<N>b[N];
int cnt=0;
struct node{
	int len;
	int u,v;
}a[N*N>>1];
inline bool cmp(node a,node b){return a.len>b.len; }
inline int dis(int i,int j){return (x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);}
int main()
{
	n=red();
	for(int re i=1;i<=n;i++)
	{
		x[i]=red(),y[i]=red();
		for(int re j=1;j<i;j++)
			a[++cnt]=(node){dis(i,j),i,j};
	}
	sort(a+1,a+cnt+1,cmp);
	for(int re i=1;i<=cnt;i++)
	{
		u=a[i].u,v=a[i].v;
		if((b[u]&b[v]).any())
		{
			printf("%.10lf",sqrt(a[i].len)/2);
			return 0;
		}
		b[u][v]=1,b[v][u]=1;
	}
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值