首页 > 其他分享 >[题解]P1452 【模板】旋转卡壳 | [USACO03FALL] Beauty Contest G

[题解]P1452 【模板】旋转卡壳 | [USACO03FALL] Beauty Contest G

时间:2024-07-18 11:29:40浏览次数:8  
标签:cur Beauty P1452 int 题解 top st vis 卡壳

P1452 【模板】旋转卡壳 | [USACO03FALL] Beauty Contest G

旋转卡壳模板题。凸包用的是Andrew算法,就不详述了,具体可以查查资料了解,但提一嘴Andrew算法的一些细节问题:

Andrew算法的一些细节

Andrew算法的模板代码如下:

sort(a+1,a+1+n,cmp);
st[++top]=1;
for(int i=2;i<=n;i++){
	while(top>=2&&cross(a[st[top]]-a[st[top-1]],a[i]-a[st[top]])>=0)
		vis[st[top--]]=0;
	vis[i]=1,st[++top]=i;
}
int tmp=top;
for(int i=n-1;i>=1;i--){
	if(vis[i]) continue;
	while(top>tmp&&cross(a[st[top]]-a[st[top-1]],a[i]-a[st[top]])>=0)
		vis[st[top--]]=0;
	vis[i]=1,st[++top]=i;
}

其中求叉积的部分,\(\ge\)和\(>\)是有区别的。\(\ge\)表示如果方向相同也要删去,结果就不会出现点在凸包边上的情况;\(>\)则是不删,结果就可能出现点在凸包边上的情况。

如果是求周长两种都可以,不过这道题使用\(>\)会很麻烦:考虑所有点都共线,这样最终结果就是所有点都在凸包上,如果不特判,下一步找直径就会出现高度相同导致的死循环(具体见下面的实现细节)。如果特判共线的话,代码又难以实现。

不如仅保留两端的点,这样我们特判条件就是\(n=3\)。


还有就是\(\ge\)和\(\le\)的区别。其实这两种的答案都是对的,只不过一个顺时针一个逆时针而已。

旋转卡壳

一个拥有\(2^4\)种读音的算法

旋转卡壳是用于解决平面最远点对问题的算法。时间复杂度为\(O(n\log n)\)。

首先我们发现,对这些点求一个凸包,最远点对一定在凸包上。而且因为是一个凸多边形,所以每个顶点到某一边的距离一定是先递增再递减的。

(注意每个顶点到某个顶点的距离并不是一定先递增再递减,考虑下图的形状。)

另外我们发现,每条边能连出的最长线段,一定是和它距离最大的顶点和它\(2\)个端点连出线段的其中之一。

所以我们可以枚举每一条边,找出到这条边距离最大的顶点(刚才已经推出距离是先递增再递减的),然后用这条边的\(2\)个端点和该点分别求距离,再更新最大值即可。

但这种做法显然是\(O(n^2)\)的。我们可以用双指针优化,因为这是凸多边形,所以考虑的边顺时针移到下一个,点一定不会逆时针走。所以每次点不需要从头开始走,从上一个位置开始走就行了。时间复杂度\(O(n)\),再加上求凸包的\(O(n\log n)\),一共是\(O(n\log n)\)。

需要特判的情况:如果凸包只有两个顶点,

该题的实现细节

实现细节:

  • 注意要输出长度的平方。然后如果直接用\(double\)求平方可能会有微小的误差,导致输出出现科学计数法,所以需要取整(显然平方后是整数)。
  • 旋转卡壳时,如果距离相同要贪心地继续取,因为相等不意味着取到最大值了。
  • 如果求完凸包,栈里有\(3\)个元素(最少就是\(3\)个),那么需要特判,否则距离相同就会不断取下一个,导致死循环。
  • 旋转卡壳时,取“\(cur\)的下一个顶点”,可以取模\(top-1\)而非\(top\),因为最后\(1\)个元素和第\(1\)个元素是相同的。

Code

凸包用Andrew算法实现。

#include<bits/stdc++.h>
#define nxtnode(x) (x%(top-1))+1
using namespace std;
struct vec{
	double x,y;
	vec operator+(const vec b){return {x+b.x,y+b.y};}
	vec operator-(const vec b){return {x-b.x,y-b.y};}
	double len(){return sqrt(x*x+y*y);}
}a[50010];
bool cmp(vec a,vec b){
	if(a.x<b.x) return 1;
	if(a.x>b.x) return 0;
	return a.y<b.y;
}
inline double cross(vec a,vec b){return a.x*b.y-a.y*b.x;}
int n,st[50010],top;
double ans;
bool vis[50010];
int main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i].x>>a[i].y;
	sort(a+1,a+1+n,cmp);
	st[++top]=1;
	for(int i=2;i<=n;i++){
		while(top>=2&&cross(a[st[top]]-a[st[top-1]],a[i]-a[st[top]])>=0)
			vis[st[top--]]=0;
		vis[i]=1,st[++top]=i;
	}
	int tmp=top;
	for(int i=n-1;i>=1;i--){
		if(vis[i]) continue;
		while(top>tmp&&cross(a[st[top]]-a[st[top-1]],a[i]-a[st[top]])>=0)
			vis[st[top--]]=0;
		vis[i]=1,st[++top]=i;
	}
	if(top==3) ans=(a[st[2]]-a[st[1]]).len();//特判
	else{
		int cur=1;
		for(int i=1;i<top;i++){
			vec bottom=a[st[i+1]]-a[st[i]];
			while(fabs(cross(bottom,a[st[nxtnode(cur)]]-a[st[i]]))>=
				fabs(cross(bottom,a[st[cur]]-a[st[i]]))){
                //这里用叉积的平四面积代替距离,因为底边是一样的,所以距离(高)和面积成正比
				cur=nxtnode(cur);
			}
			ans=max(ans,max((a[st[cur]]-a[st[i]]).len(),(a[st[cur]]-a[st[i+1]]).len()));
		}
	}
	cout<<(int)(ans*ans)<<"\n";
	return 0;
}

标签:cur,Beauty,P1452,int,题解,top,st,vis,卡壳
From: https://www.cnblogs.com/Sinktank/p/18308988

相关文章

  • 【数学建模】——多领域资源优化中的创新应用-六大经典问题解答
    目录题目1:截取条材题目 1.1问题描述1.2数学模型1.3求解1.4解答题目2:商店进货销售计划题目2.1问题描述2.2数学模型2.3求解2.4解答题目3:货船装载问题题目3.1问题重述 3.2数学模型3.3求解3.4解答题目4:城市消防站选址问题 题目4.1问题重述4.2......
  • 题解:P10733 [NOISG2019 Prelim] Lost Array
    题解:P10733[NOISG2019Prelim]LostArray思路对于任意\(\min(X_{A_{i}},X_{B_{i}})=C_{i}\)。只要让\(X_{A_{i}}\)与\(C_{i}\)取\(\max\)值。\(X_{B_{i}}\)与\(C_{i}\)取\(\max\)值。这样可以让\(\min(X_{A_{i}},X_{B_{i}})\)绝对是\(C_{i}\)。对于为赋值......
  • 题解:P10781 【MX-J1-T1】『FLA - III』Spectral
    本题的主要思路就是数学。首先,让我们先来打一个表。\(i\)\(1\)\(2\)\(3\)\(4\)\(\dots\)\(T_{i}\)\(k\)\(1.5k\)\(1.5k\)\(1.375k\)\(\dots\)易用肉眼看见,自\(T_{3}\)之后数越来越小,于是我们大胆猜测,若\(n\ne1\),则它的最大值是\(1.5k\)否则\(k\)。......
  • 题解 P1031 [NOIP2002 提高组] 均分纸牌
    link贪心题中描述每一堆牌只能移动若干张牌到相邻的牌堆上确定了局部最优解必定能推导出全局最优解。易知均分完后,每堆牌的数量都为纸牌总数的平均数\(\mathrm{arg}\)。所以我们可以预处理每堆牌跟\(\mathrm{arg}\)的差距for(inti=1;i<=n;++i)sum+=a[i];......
  • [题解]POJ3675 Telescope——求多边形与圆相交部分的面积
    POJ3675Telescope题意简述多测。每次给定一个\(N\)边形(保证相邻输入的顶点在多边形上也是邻接的),再给定一个以\((0,0)\)为圆心,半径为\(r\)的圆。请计算出多边形和圆相交部分的面积(保留\(2\)位小数)。\(3\leN\le50\)\(0.1\ler\le1000\)\(-1000\lex_i,y_i\le1000\)。......
  • ABC361-D题解
    背景保佑LC能来一中。题意给你一个长度为\(n\)的初始字符串和目标字符串,都由W和B两种字符构成。现在初始字符串末尾接有两个空格字符,每次你可以在该字符串中选出连续两个非空格字符,并把它们按顺序与两个空格交换位置。问最少需要几步能得到目标字符串。分析首先如果两......
  • ABC361-C题解
    背景昨天打比赛的时候查了中考分,心快停跳了。题意从\(n\)个数字中删除\(k\)个数字,问剩下的数字中极差的最小值。分析首先把这\(n\)个数字排序,然后问题就可以转化为求这\(n\)个数字中所有长度为\(n-k\)的连续子段的极差的最小值。采用尺取法,可以从\(1\)开始枚举......
  • 题解:AT_abc360_c [ABC360C] Move It
    背景机房大佬掉大分了,乐悲。题意给你几个箱子和每个箱子里装有的东西\(a\)和对应的重量\(w\),现在要让每个箱子里都装有一个东西,每次可以移动任意一个箱子中的任意一个东西,代价为它的重量,问最小代价。分析贪心。首先最终状态里所有箱子一定只有一个东西,那么对于所有装东西......
  • 题解:P10723 [GESP202406 七级] 黑白翻转
    背景汗流浃背了。分析容易想到一个显然的思路:以任意节点为根,开始遍历。如果一个节点的子树里面有黑点,那么它必须保留,否则如果它是白点,则可以删去。但这个方法很容易举出反例:在这颗树中,如果以最上面的白点为根,那么手推发现算法显然错误。尝试进行修改,容易发现,对于类似的情况......
  • 题解:P10722 [GESP202406 六级] 二叉树
    题意一颗\(n\)节点的二叉树,每个节点非黑即白,给你\(Q\)次操作,每次给你一个\(u\),把\(u\)的子树内所有节点颜色反转,问最终每个节点的颜色。分析看到数据范围,首先把操作离线。容易发现如果一个节点重复操作奇数次,等效于操作一次,如果重复操作偶数次,等效于没操作。所以我们可......