CQOI2016 K远点对

大意:求平面上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
#include
#include
#include
#include
#include
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[N],p[N];
LL n,m,Ans[N];
bool vst[N];
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)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;
}

你可能感兴趣的:(CQOI2016)