[NOI2005]月下柠檬树 (自适应辛普森)

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=rcre,由三角形 a c d ∼ c e g ∼ b e f acd \sim ceg \sim bef acdcegbef,由此我们可以得到 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值