您的位置:首页 > 其它

CQOI2016 K远点对

2016-04-13 16:32 190 查看
大意:求平面上k远点对

很有意思的一道题,凸包+旋转卡壳。

只会写平面上最远点对,现在要求平面上k远点对,那么总思路就是把k远点对转成最远点对。

当求到现在的最远点对(point1,point2)后,若不加处理,下次若继续求最远点对,一定还是(point1,point2),这不是我们想要的,故,要删除这个点对。删后再求最远点对,那么就是次远点对了,以此处理,最终会求得k远点对。那么问题就是删除操作,一个点对由两个点组成,任意删除一个点,这个点对将不存在,我们现在要考虑删除哪个点,我使用的极角排序,正常时候,每次凸包+卡壳的时间其实是O(nlogn+n),logn 是极角排序所带来的,从范围来看,这道题O(kn)是最合理的,显然,每次求最远点对,我不能极角排序,所以,我们要固定编号为1的点(我的代码里是y坐标最小的点中x坐标最小的)。那么删除的时候,就删除标号大的一个点,即为point2。删点的时候,考虑将会带来哪些影响,显然,图中现在存在的点和point2的距离将因为删点永远不会被后面计算到,那么我们就暴力枚举图中剩下的点,求出他们与point2的距离,保存下来,这样,就可以放心地删掉这个点了。最后的部分就是对于那些保存下来的点对距离的处理。每一次删点,理论上最多会有n个点对距离需要保存,设一个Ans[]数组,Ans[i]保存的是前面的计算过程中,第i远点对距离,所以在要保留点对的时候,如果这个距离dis小于当前的Ans[k]就没有必要保存了,每次弄完,把Ans[]排一遍,保存最大的k个,如果某一次没有任何点对距离大于Ans[k],那么break,因为当前平面上最远点已经无法更新Ans[]了,Ans[k]即为所求。代码如下。

!!!!!!!!!!!!!(PS:考试的时候我看数据肯定是n>k,就按n>k写的。。。。。。。。)

!!!!!!!!!!!!!(PS:Ans[]数组的处理其实是带log n的(懒得写堆了。。。。。。。))

#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long LL;
const double Eps=1e-10;
const LL N=200005LL,INF=100000000000000000LL;
struct point{LL x,y,i;double ag;}a
,p
;
LL n,m,Ans
;
bool vst
;
LL Dis(point p1,point p2)
{
LL x=p1.x-p2.x,y=p1.y-p2.y;
return x*x+y*y;
}
LL Cross(point p1,point p2,point p3)
{
return (p1.x-p3.x)*(p2.y-p3.y)-(p2.x-p3.x)*(p1.y-p3.y);
}
bool cmp(const point p1,const point p2)
{
if(fabs(p1.ag-p2.ag)<Eps)return Dis(a[1],p1)<Dis(a[1],p2);
return p1.ag<p2.ag;
}
void Init()
{
scanf("%lld%lld",&n,&m);
LL i,j,k;
for(i=1;i<=n;i++)scanf("%lld%lld",&a[i].x,&a[i].y);
LL Minx=INF,Miny=INF;
for(i=1;i<=n;i++)
if(a[i].y<Miny||(a[i].y==Miny&&a[i].x<Minx))
{
Miny=a[i].y;Minx=a[i].x;k=i;
}
swap(a[1],a[k]);
for(i=2;i<=n;i++)a[i].ag=atan2(a[i].y-a[1].y,a[i].x-a[1].x);
sort(a+2,a+n+1,cmp);
for(i=1;i<=n;i++)a[i].i=i;
}
bool cmp2(const LL x,const LL y){return x>y;}
void Solve()
{
LL ti,i,j,k,x,y,top,num=0,dis;
Ans[0]=-1;
for(ti=1;ti<=m;ti++)
{
top=0;
for(i=1;i<=n;i++)
{
if(vst[a[i].i])continue;
while(top>=2&&Cross(p[top],a[i],p[top-1])<=0)top--;
p[++top]=a[i];
}
dis=0;
p[0]=p[top];p[top+1]=p[1];
j=1;
for(i=1;i<=top;i++)
{
while(Dis(p[i],p[j])<=Dis(p[i],p[(j+1)%top]))j=(j+1)%top;
if(Dis(p[i],p[j])>dis)
{
dis=Dis(p[i],p[j]);
x=p[i].i;y=p[j].i;
}
}
if(x>y)swap(x,y);
vst[y]=1;
k=0;
for(i=1;i<=n;i++)
{
if(vst[i])continue;
if(Dis(a[y],a[i])>Ans[num])
{
k++;
Ans[num+k]=Dis(a[y],a[i]);
}
}
if(!k)break;
sort(Ans+1,Ans+num+k+1,cmp2);
num=min(m,num+k);
}
printf("%lld",Ans[m]);
}
int main()
{
Init();
Solve();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: