bzoj 4520 [Cqoi2016]K远点对

最远点对的话很好求,就在凸包上,旋转卡壳可以求出。
但是次远点对的话就不一定在凸包上了。
那么如何根据已有的最远点对的信息,求出次远点对,再求出三远点对……最后求出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<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
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.V<y.V;}
priority_queue<jgt>q;
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)<Dis(rt,B);
    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;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值