题解 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; }