BZOJ2458 Beijing2011最小三角形(分治)

  类似于平面最近点对,考虑分治,即分别计算分割线两侧的最小三角形再考虑跨过线的三角形。

  复杂度证明也是类似的,对于某一个点,在另一侧可能与其构成最小三角形的点在一个d*d/2的矩形内(两边之和大于第三边),并且这些点所组成的三角形周长均不小于d。然而并不清楚这里至多会有多少个点,vfk曾说上界是16,我当然不会证明这个上界也构造不出来有这么多点的方案。找这些点的时候归并就可以做到线性。那么复杂度是O(nlogn)乘上枚举这些点的常数2*16*15/2,看起来根本跑不动不过这个上界肯定是特别松的所以一点也不虚。

  算距离的时候会爆int。以及luogu数据疑似有锅。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
#define N 200010
#define inf 1000000000
int n;
double ans=inf;
struct data
{
int x,y;
bool operator <(const data&a) const
{
return x<a.x||x==a.x&&y<a.y;
}
double operator -(const data&a) const
{
return sqrt(1ll*(x-a.x)*(x-a.x)+1ll*(y-a.y)*(y-a.y));
}
}a[N],b[N],c[N];
void getans(data *b,data *c,int n,int m)
{
int s=,t=;
for (int i=;i<=n;i++)
{
while (s<=m&&c[s].y+ans/<b[i].y) s++;
while (t<m&&c[t+].y-ans/<b[i].y) t++;
for (int j=s;j<t;j++)
{
double tot=b[i]-c[j];
for (int k=j+;k<=t;k++)
ans=min(ans,tot+(c[k]-b[i])+(c[k]-c[j]));
}
}
}
void solve(int l,int r)
{
if (l==r) return;
int mid=l+r>>;
solve(l,mid);
solve(mid+,r);
int n=,m=,MID=-inf;
for (int i=l;i<=mid;i++) MID=max(MID,a[i].x);
for (int i=l;i<=mid;i++)
if (*(MID-a[i].x)<ans) b[++n]=a[i];
for (int i=mid+;i<=r;i++)
if (*(a[i].x-MID)<ans) c[++m]=a[i];
getans(b,c,n,m);
getans(c,b,m,n);
int i=l,j=mid+;
for (int k=l;k<=r;k++)
if (j>r||i<=mid&&a[i].y<a[j].y) b[k]=a[i++];
else b[k]=a[j++];
for (int k=l;k<=r;k++) a[k]=b[k];
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj2458.in","r",stdin);
freopen("bzoj2458.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
n=read();
for (int i=;i<=n;i++) a[i].x=read(),a[i].y=read();
sort(a+,a+n+);
solve(,n);
printf("%.6lf",ans);
return ;
}
上一篇:bzoj-2458 2458: [BeiJing2011]最小三角形(计算几何+分治)


下一篇:BZOJ2458:[BJOI2011]最小三角形——题解