bzoj 1502月下柠檬树 Simpson积分

关键点是,水平的圆投影到水平面之后仍然是与原先全等的圆。
然后圆与圆之间通过曲面无缝连接,所以投影下来之后圆与圆之间通过公切线连接。
直接求有点难。把投影区域的上边界当成一个函数,然后套Simpson积分就简单了。
我的实现用了指针从左往右扫,其实没必要,暴力好像还会快一点。

代码(darkbzoj 218ms):

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <unordered_set>
#include <queue>

using namespace std;

#define DEBUG 0

#define MAXN 511

typedef pair<double, int> pdi;

const double eps = 1e-8;
int sgn(double x) {
    return x < -eps ? -1 : (x > eps ? 1 : 0);
}

const double pi = acos(-1);
double cot(double x) {
    return tan(pi / 2 - x);
}

template <typename T>
T sq(T x) {
    return x * x;
}

struct Func {
    bool type;  //0: line, 1: circle
    double l, r;
    double c, R;
    bool operator < (const Func& b) const {
        return l != b.l ? l < b.l : (r < b.r);
    }
    double y(double x) const {
        if (type) {
            return sqrt(sq(R) - sq(x - c));
        } else {
            return c + (R - c) / (r - l) * (x - l);
        }
    }
};
#define fsnow const Func fs[], const unordered_set<int>& now
double Simpson(fsnow, double l, double r) {
    auto f = [&](double x) {
        double y = numeric_limits<double>::min();
        for (int i : now) {
            y = max(y, fs[i].y(x));
        }
        return y;
    };
    return (f(l) + 4 * f((l+r)/2) + f(r)) * (r - l) / 6;
};
double Integrate(fsnow, double l, double r, double eps, double pre) {
    double mid = (l+r) / 2;
    double L = Simpson(fs, now, l, mid), R = Simpson(fs, now, mid, r), delta = L + R - pre;
    return fabs(delta) <= 15*eps ? L + R + delta/15 :
           Integrate(fs, now, l, mid, eps/2, L) + Integrate(fs, now, mid, r, eps/2, R);	//If you TLE, then try not divide 2, i.e. "eps/2" -> "eps"
}
double Integrate(fsnow, double l, double r, double eps) {
    return Integrate(fs, now, l, r, eps, Simpson(fs, now, l, r));
}
double Integrate(Func fs[], int n, double eps) {
    struct REC {
        double r;
        int i;
        bool operator < (const REC& b) const {
            return r > b.r;
        }
    };
    sort(fs, fs + n);
#if DEBUG
    for (int i = 0; i < n; ++i) {
        printf("%d %f %f %f %f\n", fs[i].type, fs[i].l, fs[i].r, fs[i].c, fs[i].R);
    }
#endif
    unordered_set<int> now;
    priority_queue<REC> right;
    double l = fs[0].l, ans = 0;
    for (int i = 0; i < n; ++i) {
        while (!right.empty() && right.top().r < fs[i].l) {
            ans += Integrate(fs, now, l, right.top().r, eps);
            l = right.top().r;
            now.erase(right.top().i);
            right.pop();
        }
        ans += Integrate(fs, now, l, fs[i].l, eps);
        right.push(REC{fs[i].r, i});
        now.insert(i);
        l = fs[i].l;
    }
    while (!right.empty()) {
        ans += Integrate(fs, now, l, right.top().r, eps);
        l = right.top().r;
        now.erase(right.top().i);
        right.pop();
    }
    return ans;
}
#undef fsnow

int main() {
    int n;
    double a;
    static double x[MAXN];
    static double r[MAXN];
    static Func fs[MAXN << 1];

    scanf("%d%lf", &n, &a);
    for (int i = 0; i <= n; ++i) {
        scanf("%lf", x + i);
    }
    for (int i = 1; i <= n; ++i) {
        scanf("%lf", r + i);
    }
    x[0] = 0;
    for (int i = 1; i <= n; ++i) {
        x[i] = x[i-1] + x[i] * cot(a);
    }
#if DEBUG
    printf("%f\n", cot(a));
    for (int i = 0; i <= n; ++i) {
        printf("%f ", x[i]);
    }
    putchar('\n');
#endif
    int tot = 0;
    for (int i = 1; i <= n; ++i) {
        fs[tot++] = Func{true, x[i-1] - r[i], x[i-1] + r[i], x[i-1], r[i]};
    }
    r[n+1] = 0;
    for (int i = 1; i <= n; ++i) {
        double s = (r[i+1] - r[i]) / (x[i] - x[i-1]);
        double d1 = r[i] * s, d2 = r[i+1] * s;
        fs[tot++] = Func{false, x[i-1] - d1, x[i] - d2, sqrt(sq(r[i]) - sq(d1)), sqrt(sq(r[i+1]) - sq(d2))};
    }
    printf("%.2f\n", Integrate(fs, tot, 1e-6) * 2.0);

    return 0;
}
posted @ 2024-09-28 14:13  寻找繁星  阅读(0)  评论(0编辑  收藏  举报