题解 P3829 【[SHOI2012]信用卡凸包】
题目大意
求带圆弧卡片的凸包(凸包包含圆弧)。
关于点的旋转
设当前点为
转化
如果我们知道了圆弧如何表示(或者知道圆弧总长为定值),就可以开始乱搞计算一般多边形的凸包了。
结论:最终凸包的圆弧总长恰好等于一个给定半径为
r 的圆的周长。
证明:
#include<bits/stdc++.h>
#define db double
#define N 100005
const db pi=acos(-1);
struct point{
db x,y;
point(db _x=0,db _y=0):x(_x),y(_y){}
inline point operator-(const point&p)const{
return point(x-p.x,y-p.y);
}
inline point operator+(const point&p)const{
return point(x+p.x,y+p.y);
}
inline db dis(){return x*x+y*y;}
} p[N],sta[N];
inline db dist(point a,point b){return sqrt((a-b).dis());}
inline db cross(point a,point b,point c){
return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);
}
inline bool cmp(point a,point b){
db tmp=cross(p[1],a,b);
if(tmp>0) return 1;
if(tmp==0&&dist(p[0],a)<dist(p[0],b)) return 1;
return 0;
}
inline point rotate(point p,db angle){
db co=cos(angle),si=sin(angle);
return point(p.x*co-p.y*si,p.y*co+p.x*si);
}
int n,sz=0,top=0;
db a,b,r;
int main(){
std::cin>>n>>b>>a>>r;
db A=a/2-r,B=b/2-r;
for(int i=1;i<=n;++i){
db x,y,theta;
std::cin>>x>>y>>theta;
point center=point(x,y);
point p1=point(A,B),p2=point(A,-B),
p3=point(-A,B),p4=point(-A,-B);
p[++sz]=rotate(p1,theta)+center,p[++sz]=rotate(p2,theta)+center,
p[++sz]=rotate(p3,theta)+center,p[++sz]=rotate(p4,theta)+center;
}
for(int i=2;i<=sz;++i){
if(p[i].y<p[1].y) std::swap(p[1],p[i]);
}
std::sort(p+2,p+sz+1,cmp);
sta[++top]=p[1];
for(int i=2;i<=sz;++i){
while(top>1&&cross(sta[top],sta[top-1],p[i])>=0) --top;
sta[++top]=p[i];
} sta[top+1]=p[1];
// for(int i=1;i<=top+1;++i) printf("%.5lf %.5lf\n",sta[i].x,sta[i].y);
db ans=0;
for(int i=1;i<=top;++i) ans+=dist(sta[i],sta[i+1]);
printf("%.2lf\n",ans+2*pi*r);
return 0;
}