最远点对的话很好求,就在凸包上,旋转卡壳可以求出。
但是次远点对的话就不一定在凸包上了。
那么如何根据已有的最远点对的信息,求出次远点对,再求出三远点对……最后求出K远点对呢?
首先考虑由最远点对(p1,p2)求出次远点对。
次远点对只可能在以下三种情况中出现:
①一个点为p1,另一个点为非p1,p2的点
②一个点为p2,另一个点为非p1,p2的点
③两个点均不是p1,p2
第三种情况最好处理,直接在凸包中去掉点p1,p2,再来一次凸包+旋转卡壳就行了。虽然有n=100000个点,但由于k<=100,所以O(nk)的时间复杂度可以承受。
对于第一种情况,我们可以O(n)求出p1与其他所有点(p2除外)的距离,然后选取前K远的距离。为了方便叙述,设前K远的距离构成一个距离可重集合。
第二种情况同理。
将三种情况所产生的最远点对扔到一个大根堆里,取堆顶。
那么如何由次远点对求出三远点对呢?
如果次远点对在凸包上,处理方法基本一样。
否则次远点对就在某个距离集合里面,三远点对直接选取该集合最大值即可。
之后再由这个距离集合产生新的距离集合,扔到大根堆里。
……
如此循环K次,最后堆顶就是答案。
时间复杂度 O(nklog2k)
由于代码是考试时写的,比较难看,不太具备参考价值。
代码:
/**************************************************************
Problem: 4520
User: dropD
Language: C++
Result: Accepted
Time:12096 ms
Memory:349740 kb
****************************************************************/
#include
#include
#include
#include
using namespace std;//long long
typedef long long LL;
const LL INF=0x7fffffffffffffLL;
struct Point{LL x,y;}p[200005],rt;//点
struct Lk{LL le,ri,V,bj;}L[200005];//链表
struct jgt{LL bh,V,ck;}Q[200005];//堆
struct jgt2{LL X,V,le,ri;}mmax[100005][105];//集合
bool operator < (const jgt &x,const jgt &y){return x.Vq;
LL K,N,pos[200005],hpos[200005],del[200005],stk[200005],cnt=1,tt,XX,YY,hah;
//pos:标号->集合 hpos:集合->标号
LL Cross(Point A,Point B,Point C)
{ return (B.x-A.x)*(C.y-A.y)-(C.x-A.x)*(B.y-A.y);}
LL Dis(Point A,Point B)
{ return (A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y);}
bool cmp(const Point &A,const Point &B)
{ LL c=Cross(rt,A,B);
if(!c)return Dis(rt,A)else return c>0;
}
inline LL ne(LL x)
{ if(x==stk[0])return 1;
else return x+1;
}
void Tubao()
{ LL i;
stk[0]=0;
for(i=L[0].ri;i<=N&&i;i=L[i].ri)
{while(stk[0]>=2&&Cross(p[stk[stk[0]-1]],p[stk[stk[0]]],p[i])<=0)stk[0]--;
stk[++stk[0]]=i;
}
}
void rot(LL &ret,LL &X,LL &Y)
{ LL i,j=1;ret=0;
if(stk[0]<=1){ret=X=Y=0;return;}
for(i=1;i<=stk[0];i++)
{while(Dis(p[stk[i]],p[stk[j]])<=Dis(p[stk[i]],p[stk[ne(j)]]))j=ne(j);
if(Dis(p[stk[i]],p[stk[j]])>ret)
{ret=Dis(p[stk[i]],p[stk[j]]);
X=stk[i];Y=stk[j];
}
}
}
void Delete(LL x)
{ L[L[x].le].ri=L[x].ri;
L[L[x].ri].le=L[x].le;
L[x].bj=1;
}
void Build(LL X)
{
LL i,lim,QAQ=0;
jgt tmp;
tt=0;
for(i=1;i<=N;i++)
{tt++;
Q[tt].bh=i;Q[tt].V=Dis(p[X],p[i]);
}
sort(Q+1,Q+1+N);
lim=max(1LL,N-hah+1);
mmax[X][0].ri=1;
for(i=N;i>=lim;i--)
{QAQ++;
mmax[X][QAQ].le=QAQ-1;mmax[X][QAQ].ri=QAQ+1;
mmax[X][QAQ].X=Q[i].bh;mmax[X][QAQ].V=Q[i].V;
}
cnt++;
pos[cnt]=X;hpos[X]=cnt;
tmp.bh=X;tmp.ck=cnt;tmp.V=mmax[X][mmax[X][0].ri].V;
q.push(tmp);
}
void Delete(LL X,LL Y)//从X集合中删除Y
{ LL i;
jgt tmp;
for(i=mmax[X][0].ri;i;i=mmax[X][i].ri)
{if(mmax[X][i].X==Y)
{mmax[X][mmax[X][i].ri].le=mmax[X][i].le;
mmax[X][mmax[X][i].le].ri=mmax[X][i].ri;
break;
}
}
cnt++;
pos[cnt]=X;hpos[X]=cnt;
tmp.bh=X;tmp.ck=cnt;tmp.V=mmax[X][mmax[X][0].ri].V;
q.push(tmp);
}
int main()
{ LL i,ans,x,y;
jgt tmp;
scanf("%lld%lld",&N,&K);
hah=K+1;
for(i=1;i<=N;i++)scanf("%lld%lld",&p[i].x,&p[i].y);
//极角排序
rt.x=INF;rt.y=INF;
for(i=1;i<=N;i++)
if(rt.y>p[i].y||(rt.y==p[i].y&&rt.x>p[i].x))rt=p[i];
sort(p+1,p+1+N,cmp);
//建立链表
for(i=1;i<=N;i++){L[i].le=i-1;L[i].ri=i+1;}
L[0].ri=1;
pos[1]=0;hpos[0]=1;
//初始化
Tubao();rot(ans,XX,YY);
tmp.bh=0;tmp.V=ans;tmp.ck=1;
q.push(tmp);
while(K--)
{tmp=q.top();q.pop();
if(hpos[tmp.bh]!=tmp.ck){K++;continue;}
if(!K){printf("%lld",tmp.V);return 0;}
//最远距离在原凸包上
if(tmp.bh==0)
{Delete(XX);Delete(YY);
Build(XX);Build(YY);//建立集合
Delete(XX,YY);Delete(YY,XX);
Tubao();rot(ans,XX,YY);//重置凸包
cnt++;
tmp.bh=0;tmp.V=ans;tmp.ck=cnt;
pos[cnt]=0;hpos[0]=cnt;
q.push(tmp);//入堆
}
//不在凸包上
else
{x=tmp.bh;
y=mmax[x][0].ri;
if(!hpos[mmax[x][y].X])
{Build(mmax[x][y].X);
Delete(mmax[x][y].X);
Tubao();rot(ans,XX,YY);
cnt++;
tmp.bh=0;tmp.V=ans;tmp.ck=cnt;
pos[cnt]=0;hpos[0]=cnt;
q.push(tmp);
}
Delete(mmax[x][y].X,x);
Delete(x,mmax[x][y].X);
}
}
return 0;
}