题解 P3829 [SHOI2012]信用卡凸包

1.题意

给一些相同的信用卡,本质上是圆,求以所有信用卡的圆心为点集,求这个点集的凸包的周长。

2.思路

这题事实上没那么难。

不会凸包或者计算几何的,去看https://www.luogu.com.cn/problem/P2742,或者看看https://oi-wiki.org//geometry/的讲解。

事实上,如果信用卡是矩形,那么直接可以把矩形的所有点抽出来,跑一个凸包算法即可。这道题的难点在于信用卡是圆,而我们取出的点就有可能是一个 $\frac{1}{4}$ 圆了,也就无法正常使用凸包算法了。

说白了,我们要求出形如下图的围住的线的长度。

观察上图,容易发现,所有圆与线重合的部位就是这个圆的切线。而我们已知,圆的切线垂直于过其切点的半径。

观察每一个圆,发现重要的规律。

正如上如所示,$\alpha$ 为圆心角,因为切线垂直,发现一个对角互补四边形!

而 $\beta$ ,恰好是外角,所以 $\alpha = \beta$。

又因为多边形的外角和是 $360°$,所以 $\beta$ 加起来就是 $360°$。

所以,$\alpha$ 加起来也是 $360°$。

整个图中,所有圆都是相等的,所以,我们可以看出,所有圆连接切线而构成的扇形拼在一起就是整整一个圆。

所以问题就解决了一半,圆弧的总长就是 $2πr$ 。

剩下的问题就是,求一下所有直边的长度。

根据矩形平移,我们非常容易地得出,所有圆心点求一个凸包就是所有直边的长度。

直边长加上圆的周长,即为答案,至此,本题做完。

3.代码

 

#include<bits/stdc++.h>
using namespace std;
int n,tx[]={1,1,-1,-1},ty[]={1,-1,1,-1},cnt,stk[100005],top;
bool vis[100005];
double a,b,r,xx,yy,zz,res;
pair<double,double> q[100005];
pair<double,double> rotate(pair<double,double> a,double b){
	return {a.first*cos(b)+a.second*sin(b),-a.first*sin(b)+a.second*cos(b)};
}
pair<double,double> operator-(pair<double,double> a,pair<double,double> b){
    return {a.first-b.first,a.second-b.second};
}
double cross(pair<double,double> a,pair<double,double> b){
	return a.first*b.second-a.second*b.first;
}
double area(pair<double,double> a,pair<double,double> b,pair<double,double> c){
    return cross(b-a,c-a);
}
double get_dist(pair<double,double> a,pair<double,double> b){
	double dx=a.first-b.first,dy=a.second-b.second;
    return sqrt(dx*dx+dy*dy);
}
int main(){
	scanf("%d%lf%lf%lf",&n,&a,&b,&r);
	a=a/2-r;
	b=b/2-r;
	for(int i=1;i<=n;i++){
		scanf("%lf%lf%lf",&xx,&yy,&zz);
		for(int i=0;i<4;i++){
			pair<double,double> temp=rotate({tx[i]*b,ty[i]*a},-zz);
			q[cnt++]={temp.first+xx,temp.second+yy};
		}
	}
	sort(q,q+cnt);
	for(int i=0;i<cnt;i++){
		while(top>=2&&area(q[stk[top-1]],q[stk[top]],q[i])>=0){
			vis[stk[top--]]=0;
		}
		stk[++top]=i;
		vis[i]=1;
	}
	vis[0]=0;
	for(int i=cnt-1;i>=0;i--){
		if(vis[i]){
			continue;
		}
		while(top>=2&&area(q[stk[top-1]],q[stk[top]],q[i])>=0){
			top--;
		}
		stk[++top]=i;
	}
	for(int i=2;i<=top;i++){
		res+=get_dist(q[stk[i-1]],q[stk[i]]);
	}
	printf("%.2lf",res+2*3.141592653589793*r);
	return 0;
}

  

posted @ 2022-05-02 07:36  21xf2257  阅读(72)  评论(1编辑  收藏  举报