题解 P3829 【[SHOI2012]信用卡凸包】 Link Solve code

P3829 [SHOI2012]信用卡凸包

Solve

话说这道题挺良心的,算法在题目上都写出来了,我们就从凸包来考虑。

我们发现有圆的地方很难处理,画了几个图后发现,相比之下,圆的地方加起来就是一个整圆,问题是怎么来证明。

这里借用一下神仙jun头吉吉的图

题解 P3829 【[SHOI2012]信用卡凸包】
Link
Solve
code

我们观察到多出来的部分和圆肯定是相切的,比如(DK)切与圆(C),所以(∠BCD+∠BZD=180°)

多边形内角和为(sumlimits_{i=1}^nalpha_i=180 imes (n-2))

所以所有圆心角之和为$180 imes n - sumlimits_{i=1}^nalpha_i =180 imes n -180 imes (n-2) = 360 = 2pi $

答案即为一个圆的周长+圆心构成的凸包的周长。

所以这道题就和板子一样了

code

#include<bits/stdc++.h>
using namespace std;
const int maxn=100005;
int n,top,m;
double ans=0,A,B,R,l,phi;
struct AS{
	double x,y;
}a[maxn],p[maxn];
inline int read(){
	int ret=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
	while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
	return ret*f;
}
double check(AS A1,AS A2,AS B1,AS B2){
	return (A2.x-A1.x)*(B2.y-B1.y)-(B2.x-B1.x)*(A2.y-A1.y);
}
double d(AS A,AS B){
	return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));
}
bool cmp(AS x,AS y){
	double tmp=check(a[1],x,a[1],y);
	if(tmp>0)return 1;
	if(tmp==0&&(d(a[1],x)<d(a[1],y)))return 1;
	return 0;
}
void add(double x,double y){
	n++;	
	a[n].x=x;a[n].y=y;
	if(a[n].y<a[1].y)swap(a[1],a[n]);
}
int main(){
	freopen("P3829.in","r",stdin);
	freopen("P3829.out","w",stdout);
	scanf("%d%lf%lf%lf",&m,&A,&B,&R);A-=2*R;B-=2*R;
	l=sqrt(A*A+B*B)/2;
	phi=atan(A/B);
	for(int i=1;i<=m;i++){
		double x,y,theta;
		scanf("%lf%lf%lf",&x,&y,&theta);
		double dx=cos(theta+phi)*l;
		double dy=sin(theta+phi)*l;
		add(x+dx,y+dy);
		add(x-dx,y-dy);
		dx=cos(theta-phi)*l;
		dy=sin(theta-phi)*l;
		add(x+dx,y+dy);
		add(x-dx,y-dy);
	}
	sort(a+2,a+1+n,cmp);
	p[top=1]=a[1];
	for(int i=2;i<=n;i++){
		while(top>1&&check(p[top-1],p[top],p[top],a[i])<=0)top--;
		top++;
		p[top]=a[i];
	}
	p[++top]=p[1];
	for(int i=1;i<top;i++){
		ans+=d(p[i],p[i+1]);
	}
	printf("%.2lf
",ans+3.141592653589793*2*R);
	return 0;
}