【洛谷1452】【模板】旋转卡壳

点此看题面

  • 给定平面上\(n\)个点,要求你求出它们所构成凸包的直径。
  • \(n\le5\times10^4\)

旋转卡壳模板

一些基础的定义(例如切线、对踵点之类的)就懒得写了。

据说旋转卡壳一般来说都有两种写法,而我自然选择的是其中相对简单的那一种。

考虑如果我们找到了最优状态下的一对切线,把它们同时旋转到与这两个点的某一条邻边相切,那么对面的那个点,必然是与这条边距离最大的点。

然后就可以发现一个很好的性质,凸包上每个点与一条边的距离是满足先增后减的,也就是具有单调性。

而且,如果我们按顺序枚举每一条边,最远的那个点必然是满足单调移动的。

因此我们只要在枚举边的同时利用双指针维护好最远的点就可以做到\(O(n)\)复杂度了(然而求凸包的复杂度是\(O(nlogn)\)的)。

在边确定的时候,要比较距离的大小,其实可以比较与这条边一起构成的三角形面积的大小,直接叉积计算即可。

代码:\(O(nlogn)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 50000
using namespace std;
int n;struct P
{
	int x,y;I P(CI a=0,CI b=0):x(a),y(b){}
	I P operator + (Con P& o) Con {return P(x+o.x,y+o.y);}
	I P operator - (Con P& o) Con {return P(x-o.x,y-o.y);}
	I int operator ^ (Con P& o) Con {return x*o.y-y*o.x;}
	I bool operator < (Con P& o) Con {return x^o.x?x<o.x:y<o.y;}
	I int L2() {return x*x+y*y;}
}p[N+5];
int T;P s[N+5];I void Get()//求凸包
{
	#define pd(A,B,C) (((C-B)^(B-A))>0||(((C-B)^(B-A))==0&&(A<B)==(B<C)))//平行也需要弹去
	RI i;for(sort(p+1,p+n+1),i=1;i<=n;s[++T]=p[i++]) W(T>1&&pd(s[T-1],s[T],p[i])) --T;
	for(i=n-1;i;s[++T]=p[i--]) W(T>1&&pd(s[T-1],s[T],p[i])) --T;--T;
}
I void Rotate()//旋转卡壳
{
	RI i,j,t=(p[1]-p[n]).L2();for(i=1,j=3;i<=T;++i)//枚举每一条边
	{
		W(((s[i+1]-s[i])^(s[j]-s[i]))<((s[i+1]-s[i])^(s[j%T+1]-s[i]))) j=j%T+1;//双指针维护最远点
		t=max(t,max((s[j]-s[i]).L2(),(s[j]-s[i+1]).L2()));//与两端点距离
		t=max(t,max((s[j%T+1]-s[i]).L2(),(s[j%T+1]-s[i+1]).L2()));//最远点可能有两个
	}printf("%d\n",t);
}
int main()
{
	RI i;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d%d",&p[i].x,&p[i].y);
	return Get(),Rotate(),0;
}
上一篇:DBHelper


下一篇:上传网络图片到微信临时素材换取mediaId