最近点对 HDU5721

最近点对:
对所有点按照 x坐标排序。然后分治,求出左半边和右半边的最近点对,对于两边的最近距离取较小的,记为 d。取从分治的中间点向左右各距离为 d d的范围中的所有点,把这些点按照 y坐标排序。对于每个点,扫一下与它 y坐标差小于等于 d的点,更新答案,可以证明(证明没看懂=_=)这样的点是很少的,不超过6个。

该题题意:给定n个点的坐标,第i个点消失后的最近点对距离为di,求di的和。

这题解法: 因为如果消失的点不是最近点对,那么对答案是没有影响的。
所以可以先求出没消失之前的最近点距离*(n-2),(n-2)指的是消失的点不是最近点对。再分别求两个最近点消失后的最近点距离。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
#define x first
#define y second
using namespace std;
typedef long long LL;
struct point
{
    int x,y;
}p[100005];
int tmp[100005];
int A,B;
LL mn=1ll<<54;
bool cmpxy(point a,point b)
{
    if(a.x!=b.x) return a.x<b.x;
    else return a.y<b.y;
}
bool cmpy(int a,int b)
{
    return p[a].y<p[b].y;
}
LL dis(point a,point b)
{
    return (LL)(a.x-b.x)*(a.x-b.x)+(LL)(a.y-b.y)*(a.y-b.y);
}
void update(int a,int b)//更新[1,n]的最近点对
{
    if(mn>dis(p[a],p[b])){mn=dis(p[a],p[b]);A=a;B=b;}
}
LL closest(int L,int R)
{
    if(L==R) return 1ll<<50;
    if(L+1==R) {update(L,R);return dis(p[L],p[R]);}
    int mid=(L+R)>>1;
    LL ret=min(closest(L,mid),closest(mid+1,R));
    int k=0;
    for(int i=L;i<=R;i++)
    {
        if(1ll*(p[mid].x-p[i].x)*(p[mid].x-p[i].x)<=ret)
            tmp[k++]=i;//存符合条件的点的下标
    }
    sort(tmp,tmp+k,cmpy);
    for(int i=0;i<k;i++)
        for(int j=i+1;j<k&&1ll*(p[tmp[j]].y-p[tmp[i]].y)*(p[tmp[j]].y-p[tmp[i]].y)<=ret;j++)
        {
            update(tmp[i],tmp[j]);
            ret=min(dis(p[tmp[i]],p[tmp[j]]),ret);
        }
    return ret;
 /*ret为[L,R]区间的最近点距离的d^2,所以前面都是类似这种
 (p[mid].x-p[i].x)*(p[mid].x-p[i].x)<=ret    
用来表示x坐标差小于d
}
int main()
{
#ifdef LOCAL
    freopen("in.txt","r",stdin);
#endif // LOCAL
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        LL ans=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d%d",&p[i].x,&p[i].y);
        sort(p+1,p+1+n,cmpxy);
        closest(1,n);
        ans+=mn*(n-2);
        mn=1ll<<50;
        int a=A,b=B;
        for(int i=1;i<=n;i++)
            if(i==a) swap(p[i],p[n]);
        closest(1,n-1);
        ans+=mn;
        mn=1ll<<50;
        for(int i=1;i<=n;i++)
            if(i==b) swap(p[i],p[n]);
        closest(1,n-1);
        ans+=mn;
        mn=1ll<<50;
        cout<<ans<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值