P4207 [NOI2005]月下柠檬树
如图,我们要求的面积就是这些圆形跟梯形的组合,由于投射到地面上,显然有 h ′ = h t a n θ h' = \frac{h}{tan \theta} h′=tanθh,由此我们就可以开始推导这个 f ( x ) f(x) f(x)函数了。
所以转换为我们要推导出直线 a , b a, b a,b的函数表达式了。
有公切线 a b ab ab,矩形 a b g c abgc abgc,所以显然有 e g = r c − r e eg = r_c - r_e eg=rc−re,由三角形 a c d ∼ c e g ∼ b e f acd \sim ceg \sim bef acd∼ceg∼bef,由此我们可以得到 c d , e f cd, ef cd,ef从而得到 a d , b f ad, bf ad,bf,知道这里对圆公切线问题就已经解决了。
所以接下来我们知道带到函数中利用自适应辛普森求解即可,详细细节见代码描述。
/*
Author : lifehappy
*/
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const double eps = 1e-6;
const int N = 510;
int n;
double angle;
struct Point {
double x, y;
}circle[N], line[N << 1];
void get_point() {//求得切线的左右两个坐标。
for(int i = 1; i < n; i++) {
double x1 = circle[i].x, x2 = circle[i + 1].x, r1 = circle[i].y, r2 = circle[i + 1].y;
double ce = r1 * (r1 - r2) / (x2 - x1), xa = x1 + ce, ya = sqrt(r1 * r1 - ce * ce);
double gd = r2 * (r1 - r2) / (x2 - x1), xb = x2 + gd, yb = sqrt(r2 * r2 - gd * gd);
line[2 * i - 1] = {xa, ya}, line[2 * i] = {xb, yb};
}
}
double f(double x) {
double ans = 0.0;
for(int i = 1; i <= n; i++) {//在圆里面,
if(x < circle[i].x + circle[i].y && x > circle[i].x - circle[i].y) {
ans = max(ans, sqrt(circle[i].y * circle[i].y - (x - circle[i].x) * (x - circle[i].x)));
}
}
for(int i = 1; i <= 2 * (n - 1); i += 2) {//被切线包围,
if (x >= line[i].x && x <= line[i + 1].x) {
ans = max(ans, (line[i + 1].y - line[i].y) / (line[i + 1].x - line[i].x) * (x - line[i].x) + line[i].y);
}
}
//这些值一定是取最大嘛,因为它可能同时符合圆,切线的要求,所以我们得取覆盖面积最大的。
return ans;
}
double sim(double l, double r) {
return (r - l) * (f(l) + f(r) + f((l + r) / 2.0) * 4.0) / 6.0;
}
double asr(double l, double r, double eps, double ans) {
double mid = (l + r) / 2.0;
double ansl = sim(l, mid), ansr = sim(mid, r);
if(fabs(ansl + ansr - ans) < 15.0 * eps) return ansl + ansr + (ansl + ansr - ans) / 15.0;
return asr(l, mid, eps / 2.0, ansl) + asr(mid, r, eps / 2.0, ansr);
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
// ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
scanf("%d %lf", &n, &angle);
angle = tan(angle);
n++;
for(int i = 1; i <= n; i++) {
scanf("%lf", &circle[i].x);
circle[i].x /= angle;//改变x的值,求个前缀和得到对应的准确的坐标。
circle[i].x += circle[i - 1].x;
}
for(int i = 1; i < n; i++) {
scanf("%lf", &circle[i].y);
}
circle[n].y = 0;
get_point();
double l = circle[1].x - circle[1].y, r = circle[n].x;//确定积分区间。
for(int i = 1; i <= n; i++) {
l = min(l, circle[i].x - circle[i].y);
r = max(r, circle[i].x + circle[i].y);
}
printf("%.2f", 2.0 * asr(l, r, eps, sim(l, r)));
return 0;
}