弧线不好处理,考虑先求出直线的总长
画个图发现,把直线向内移动 $r$ 以后,所有直线构成了圆心点集的凸包
然后考虑弧线的长度,容易发现弧线的长度总是圆的周长,大概证明就是直线需要经过弧线才能拐弯
因为最后拐回来了,所以绕了一圈,那么弧线的弧度总和就是 $2pi$
然后求所有圆心的凸包加上圆周长就好了
注意凸包的精度问题,我代码 $eps=1e-12$ 就会 $GG$,$eps=1e-5$ 就好了
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; typedef long long ll; typedef double db; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const db eps=1e-5,pi=acos(-1.0); const int N=1e5+7; inline int dcmp(db x) { if(fabs(x)<eps) return 0; return x<0 ? -1 : 1; } struct Poi { db x,y; Poi (db a=0,db b=0) { x=a,y=b; } inline Poi operator + (const Poi &tmp) const { return Poi(x+tmp.x,y+tmp.y); } inline Poi operator - (const Poi &tmp) const { return Poi(x-tmp.x,y-tmp.y); } inline bool operator < (const Poi &tmp) const { return dcmp(x-tmp.x)!=0 ? x<tmp.x : y<tmp.y; } }P[N],st[N],T[5]; inline db Cross(Poi A,Poi B) { return A.x*B.y-A.y*B.x; } inline db Dot(Poi A,Poi B) { return A.x*B.x+A.y*B.y; } inline db Len(Poi A) { return sqrt(Dot(A,A)); } inline bool cmp(const Poi &A,const Poi &B) { return dcmp(Cross(A,B))>0|| (dcmp(Cross(A,B))==0&&Len(A)<Len(B) ); } inline Poi rotate(Poi A,db a) { return Poi(A.x*cos(a)-A.y*sin(a) , A.x*sin(a)+A.y*cos(a)); } int n,tot; inline void ins(db x,db y,db a) { for(int i=1;i<=4;i++) P[++tot]=Poi(x,y)+rotate(T[i],a); } db Tubao() { int Top=0; sort(P+1,P+tot+1); for(int i=tot;i>=1;i--) P[i]=P[i]-P[1]; sort(P+1,P+tot+1,cmp); for(int i=1;i<=tot;st[++Top]=P[i],i++) while( Top>1 && dcmp(Cross(P[i]-st[Top-1],st[Top]-st[Top-1]))>=0 ) Top--; db res=Len(st[Top]-st[1]); for(int i=2;i<=Top;i++) res+=Len(st[i]-st[i-1]); return res; } int main() { n=read(); db a,b,r,ans; scanf("%lf%lf%lf",&a,&b,&r); ans=pi*r*2; a-=r*2,b-=r*2; T[1]=Poi(b/2,a/2); T[2]=Poi(b/2,-a/2); T[3]=Poi(-b/2,a/2); T[4]=Poi(-b/2,-a/2); for(int i=1;i<=n;i++) scanf("%lf%lf%lf",&a,&b,&r),ins(a,b,r); printf("%.2lf ",ans+Tubao()); return 0; }