题解 P3829 [SHOI2012]信用卡凸包
1.题意
给一些相同的信用卡,本质上是圆,求以所有信用卡的圆心为点集,求这个点集的凸包的周长。
2.思路
这题事实上没那么难。
不会凸包或者计算几何的,去看https://www.luogu.com.cn/problem/P2742,或者看看https://oi-wiki.org//geometry/的讲解。
事实上,如果信用卡是矩形,那么直接可以把矩形的所有点抽出来,跑一个凸包算法即可。这道题的难点在于信用卡是圆,而我们取出的点就有可能是一个 圆了,也就无法正常使用凸包算法了。
说白了,我们要求出形如下图的围住的线的长度。
观察上图,容易发现,所有圆与线重合的部位就是这个圆的切线。而我们已知,圆的切线垂直于过其切点的半径。
观察每一个圆,发现重要的规律。
正如上如所示, 为圆心角,因为切线垂直,发现一个对角互补四边形!
而 ,恰好是外角,所以 。
又因为多边形的外角和是 ,所以 加起来就是 。
所以, 加起来也是 。
整个图中,所有圆都是相等的,所以,我们可以看出,所有圆连接切线而构成的扇形拼在一起就是整整一个圆。
所以问题就解决了一半,圆弧的总长就是 。
剩下的问题就是,求一下所有直边的长度。
根据矩形平移,我们非常容易地得出,所有圆心点求一个凸包就是所有直边的长度。
直边长加上圆的周长,即为答案,至此,本题做完。
3.代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | #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; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】