题面
一棵合法的柠檬树如图,其中除最顶层外,每一层都是一个圆台,最顶层是一个圆锥:

给定 hi,ri,α,求其在月光下投影的面积,保留两位小数。
n⩽500,0.3<α<π2。
题解
单独的圆锥
这同时也是 subtask1 这个部分分的条件。考虑一个圆锥的投影:

根据投影知识,不难得到,AJ=tanαAD。
我们可以直接得到 J 的坐标,投影的面积可以用 △JKM 的面积和扇形 KCG 的面积的和的两倍表示。所以唯一的问题就是:已知 J,C 坐标,⊙C 的半径,求 S△JKC,∠KCG。因为 JC,r 已知,不难得到 ∠KCJ 和它的三角函数,进而解决问题,不多赘述。
单独的圆台
考虑单独的一个圆台怎么做(geogebra 画不出圆台,只能用两个圆简陋表示一下):

显然,E,F 坐标都能求出来,且 ⊙E 的半径 r1,⊙F 的半径 r2 都知道。为了方便,画出俯视图:

设两条外公切线交于点 A。有:
sin∠EAK=AFFG=AF+EFEK
FG,EK,EF 为已知,可以解出 AF 的值,然后 A 的坐标和 ∠EAK 的三角函数就出来了,于是能求出公切线的解析式,于是 G,K 坐标都可以求出来了。
还是只考虑它的一半,不难发现就是直角梯形 GKEF 的面积加上两个扇形。都可以由已知量简单推导得到。
我们联合!
考虑这两个东西加在一起会产生什么效应:

题目瞬间感觉毒瘤了不少。我们还是只考虑一半的情况,发现这个东西是可以分割的,于是作这些切点关于 y 轴的垂线:

至于梯形部分,和上面圆台的解决方法是一样的。唯一的三角形和圆锥的解决方法类似。因此重点考虑“斜边”是一段弧的东西。即图中类似于封闭图形 DHUN 的部分。
法一

所求面积为:
S扇形FAC−S△AEC−S扇形FAB+S△ADB
C 在 A 下方时同理。
法二
因为圆一定在 y 轴上,所以圆的解析式为 r2=x2+(y−a)2。
为了方便我们把 x,y 反过来,让它落在 x 轴上,解析式变为 r2=(x−a)2+y2。先考虑 a=0 的情况,后续再把 x 用 x−a 代进去。
y=r2−x2
求它的一个不定积分:
∫r2−x2dx
我们知道 −r⩽x⩽r,所以 x 可以表示为 rsinφ 的形式:
∫r2−r2sin2φdrsinφ
然后我们发现根号里面的东西就是 r2cos2φ:
∫rcosφdrsinφ=∫r2cosφ(sinφ)′dφ
(sinx)′=cosx,r 为常数,可以提出来
r2∫cos2φdφ
诱导公式变一下,再提一波常数:
2r2∫1+cos2φdφ
和的积分等于积分的和,考虑第二项怎么积分,看成 f(x)=cosx,g(x)=2x 的复合函数即可:
2r2(φ+2sin2φ)
然后把 x 代回去,二倍角公式展开三角函数,φ=arcsinrx:
2r2(arcsinrx+rx×1−r2x2)
然后化简一下就可以了:
2r2arcsinrx+2xr2−x2
记得把 x−a(a 为圆心横坐标)代到 x 中而不是直接用 x:
2r2arcsinrx−a+2(x−a)r2−(x−a)2
于是求那个东西的面积的时候,假设弧的端点横坐标分别为 x1,x2,将其分别代入然后减一下即可。
细节
这一段我一直没有讨论全,看了 iuyi 大佬的题解后豁然开朗%%%。
一些细节问题不拿出来单独讨论会直接出错,例如两个圆之间没有公切线(包含关系)的情况:

针对这个图,我们添加一个判断:
- 把最上面和最下面的点也视为分段点。
- 如果一个段内同时有多个圆弧或线段,取它们面积的最大值为这一段的面积。
但是我们又发现上面的图有些极端,不一定存在一个大圆包含了所有其它的圆,也不一定是圆心坐标大的点包含圆心坐标小的点。最为保险的还是添加两条:
-
圆和圆、线段之间的交点、线段和线段的交点,也记作分段的点。
-
如果一个圆包含下一个圆(按圆心坐标大小枚举时),不要计算公切线。
圆和圆的交点、线段和线段的交点比较简单,圆和线段的交点直接联立解析式都能做。
还有就是注意精度误差产生的问题,有些地方不加限制条件会爆 WA(特别是代码中用注释提到的那一段)。
于是这个题就做出来了,剩下的都交给代码实现了 qwq。
代码
有借鉴于 @iuyi 的题解代码,仅供参考。
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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
| #include<bits/stdc++.h> #define double long double using namespace std; const int max_n=502; const double inf=1000000000.0;
inline double Rt(double a,double b,double c){ if(b==inf) swap(a,b); if(c==inf) return sqrt(a*a+b*b); return sqrt(c*c-b*b); }
int n,cnt; double alpha,h[max_n],r[max_n]; double dn[max_n],up[max_n];
struct dot{ double x,y; }p[max_n*max_n*10];
inline dot maked(double a,double b){ dot res; res.x=a,res.y=b; return res; }
double sep[max_n*max_n];
inline void Circ_Circ(){ for(register int i=1;i<=n;++i) for(register int j=i+1;j<=n;++j) if(dn[i]<dn[j] && up[i]>dn[j] && up[i]<up[j]) sep[++cnt]=h[i]-((r[j]*r[j]-r[i]*r[i])/(h[j]-h[i])-h[j]+h[i])/2; }
struct line{ dot a,b; inline bool valid(){ if(a.x==inf || a.y==inf) return 0; if(b.x==inf || b.y==inf) return 0; return 1; } inline double slope(){ if(a.x==b.x) return inf; return (a.y-b.y)/(a.x-b.x); } }l[max_n];
inline line makel(dot a,dot b){ line res; res.a=a,res.b=b; return res; }
inline void Line_Line(){ for(register int i=1;i<=n;++i) for(register int j=i+1;j<=n;++j) if(l[i].valid() && l[j].valid()){ double k1=l[i].slope(),k2=l[j].slope(); if(k1==inf || k2==inf || k1==k2) continue; double b1=l[i].a.y-k1*l[i].a.x,b2=l[j].a.y-k2*l[j].a.x; double X=(b2-b1)/(k1-k2); if(X>=l[i].a.x && X<=l[i].b.x && X>=l[j].a.x && X<=l[j].b.x) sep[++cnt]=X; } }
inline void Circ_Line(){ for(register int i=1;i<=n;++i) for(register int j=1;j<=n;++j) if(j!=i && j!=i-1 && l[j].valid()){ double k=l[j].slope(),b=l[j].a.y-k*l[j].a.x; double A=k*k+1,B=2*k*b-2*h[i],C=b*b+h[i]*h[i]-r[i]*r[i],delta=B*B-4*A*C; if(delta<0) continue; double x1=(-B+sqrt(delta))/(A*2),x2=(-B-sqrt(delta))/(A*2); if(x1>=l[j].a.x && x1<=l[j].b.x) sep[++cnt]=x1; if(x2>=l[j].a.x && x2<=l[j].b.x) sep[++cnt]=x2; } }
double ans=0;
inline double S_Line(double x1,double x2,int i){ x1=max(x1,l[i].a.x), x2=min(x2,l[i].b.x); if(x1>=x2) return 0; double k=l[i].slope(),b=l[i].a.y-k*l[i].a.x; if(k==inf) return 0; return (k*x1+b+k*x2+b)*(x2-x1)/2; }
inline double Calc(double x,double a,double r){ x-=a; return r*r/2*asin(max((double)-1,min((double)1,x/r)))+x*sqrt(max((double)0,r*r-x*x))/2; }
inline double S_Circ(double x1,double x2,int i){ x1=max(x1,dn[i]), x2=min(x2,up[i]); if(x1>=x2) return 0; return Calc(x2,h[i],r[i])-Calc(x1,h[i],r[i]); }
signed main(){
scanf("%d%Lf",&n,&alpha); alpha=1.0/tan(alpha); for(register int i=1;i<=n+1;++i){ scanf("%Lf",&h[i]); h[i]=h[i]*alpha+h[i-1]; } sep[++cnt]=inf, sep[++cnt]=-inf; for(register int i=1;i<=n+1;++i){ if(i<=n) scanf("%Lf",&r[i]); dn[i]=h[i]-r[i], up[i]=h[i]+r[i]; sep[1]=min(sep[1],dn[i]), sep[2]=max(sep[2],up[i]); } Circ_Circ(); for(register int i=1;i<=n;++i){ if(up[i]>=up[i+1] || dn[i]>=dn[i+1]){ l[i]=makel(maked(inf,inf),maked(inf,inf)); continue; } if(r[i]==r[i+1]){ sep[++cnt]=h[i], sep[++cnt]=h[i+1]; l[i]=makel(maked(h[i],r[i]),maked(h[i+1],r[i+1])); } else if(r[i+1]==0){ double delta=r[i]*r[i]/(h[i+1]-h[i]); sep[++cnt]=h[i]+delta, sep[++cnt]=h[i+1]; l[i]=makel(maked(h[i]+delta,Rt(delta,inf,r[i])),maked(h[i+1],0)); } else{ double AF=r[i+1]*(h[i+1]-h[i])/(r[i]-r[i+1]); double Gx=h[i+1]+r[i+1]*r[i+1]/AF,Kx=h[i]+r[i]*r[i+1]/AF; sep[++cnt]=Kx, sep[++cnt]=Gx; l[i]=makel(maked(Kx,Rt(r[i]*r[i+1]/AF,inf,r[i])),maked(Gx,Rt(r[i+1]*r[i+1]/AF,inf,r[i+1]))); } } Line_Line(), Circ_Line(); sort(sep+1,sep+1+cnt), cnt=unique(sep+1,sep+1+cnt)-sep-1; for(register int i=1;i<cnt;++i) if(sep[i]!=sep[i+1]){ double mx=0; for(register int j=1;j<=n;++j){ mx=max(mx,S_Line(sep[i],sep[i+1],j)), mx=max(mx,S_Circ(sep[i],sep[i+1],j)); } ans+=mx; } printf("%.2Lf",ans*2); return 0; }
|