传送门
可以发现答案就是所有信用卡四个角的圆心做出来的凸包加上一个整的圆的周长
证明?
由于直线的斜率不会变,所以只有可能在圆的边上绕的时候改变
把所有圆围起来就是一个整圆了
剩下的在边上和圆心无所谓(思考)
然后凸包就完了
关于向量旋转角度的变换随便找一篇blog或者手推一下就出来了
#include<bits/stdc++.h>
using namespace std;
#define eps 1e-8
inline int read(){
char ch=getchar();
int res=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
return res*f;
}
const int N=40005;
const double pi=acos(-1.0);
struct point{
double x,y;
point(double a=0,double b=0){
x=a,y=b;
}
friend inline point operator -(const point &a,const point &b){
return point(a.x-b.x,a.y-b.y);
}
friend inline point operator +(const point &a,const point &b){
return point(a.x+b.x,a.y+b.y);
}
friend inline double operator *(const point &a,const point &b){
return a.x*b.y-a.y*b.x;
}
inline double calc()const{
return x*x+y*y;
}
}p[N],q[N];
inline point rotate(point a,double rad){
return point(a.x*cos(rad)-a.y*sin(rad),a.x*sin(rad)+a.y*cos(rad));
}
int n,num,m;
double a,b,r,ans;
inline bool comp(const point &a,const point &b){
double res=(a-p[1])*(b-p[1]);
if(res!=0)return res>0;
return (a-p[1]).calc()<(b-p[1]).calc();
}
inline void graham(){
int pt=1;
for(int i=2;i<=num;i++){
if(p[i].x<p[pt].x||p[i].x==p[pt].x&&p[i].y<p[pt].y)pt=i;
}
if(pt!=1)swap(p[pt],p[1]);
sort(p+2,p+num+1,comp);
q[++m]=p[1];
for(int i=2;i<=num;i++){
while(m>=3&&(p[i]-q[m-1])*(q[m]-q[m-1])>=0)m--;
q[++m]=p[i];
}
}
int main(){
n=read();
scanf("%lf%lf%lf",&a,&b,&r);
a-=2*r,b-=2*r;
for(int i=1;i<=n;i++){
double x,y,rad;
scanf("%lf%lf%lf",&x,&y,&rad);
point k(x,y),d,v;
d=point(x+b/2,y-a/2),v=rotate(k-d,rad),p[++num]=k+v;
d=point(x-b/2,y-a/2),v=rotate(k-d,rad),p[++num]=k+v;
d=point(x+b/2,y+a/2),v=rotate(k-d,rad),p[++num]=k+v;
d=point(x-b/2,y+a/2),v=rotate(k-d,rad),p[++num]=k+v;
}
graham();
for(int i=2;i<=m;i++)
ans+=sqrt((q[i]-q[i-1]).calc());
ans+=sqrt((q[1]-q[m]).calc());
ans+=2*pi*r;
printf("%.2lf",ans);
}