[BZOJ2178]圆的面积并

[BZOJ2178]圆的面积并

试题描述

给出 \(N\) 个圆,求其面积并

输入

先给一个数字 \(N\), \(N \le 1000\) 接下来是 \(N\) 行是圆的圆心,半径,其绝对值均为小于 \(1000\) 的整数

输出

面积并,保留三位小数

输入示例 & 输出示例

数据规模及约定

见“输入

题解

裸的 Simpson 积分,可以用来解决一般的连续函数的积分。

Simpson 积分是这样一个式子

\[ \int_l^r f(x) \mathrm{d} x \approx \frac{1}{6}(r-l)(f(l)+f(r)+4f(\frac{l+r}{2})) \]

对于 \(2\) 次及以下的函数上面的式子求出来的是精确值。

无聊的话可以证一下,令 \(f(x) = ax^2 + bx + c\)

\begin{aligned}
\int_l^r f(x) \mathrm{d} x
& = \int_l^r (ax^2 + bx + c) \mathrm{d}x \\
& = \left( \frac{1}{3} ar^3 + \frac{1}{2} br^2 + cr \right) - \left( \frac{1}{3} al^3 + \frac{1}{2} bl^2 + cl \right) \\
& = \frac{1}{3} a (r-l)(r^2 + lr + l^2) + \frac{1}{2} b (r-l)(r+l) + c(r-l) \\
& = \frac{1}{6} (r-l) ( 2ar^2 + 2alr + 2al^2 + 3br + 3bl + 6c ) \\
& = \frac{1}{6} (r-l) \left[ (ar^2 + br + c) + (al^2 + bl + c) + 4a \left( \frac{r}{2} \right)^2 + 4a\left( \frac{l}{2} \right)^2 + 4b\left( \frac{r}{2} \right) + 4b\left( \frac{l}{2} \right) + 4c \right] \\
& = \frac{1}{6} (r-l) \left[ f(r) + f(l) + 4f \left( \frac{l+r}{2} \right) \right]
\end{aligned}

对于这道题,想象一条竖直扫描线从左到右扫过整个图形,那么答案其实就是对“直线在图形内部的长度”进行积分,套用上面的公式就可以得出大致的答案了;为了提高精度,我们可以进行分治,对于横坐标在 \([l, r]\) 内的部分计算一下 \([l, r]\) 的答案,看和 \([l, m]\)\([m, r]\)\(m = \frac{l + r}{2}\))的答案加起来是否相差很小,如果不够小则继续分治直接求 \([l, m]\)\([m, r]\) 的和。

如何求出“在图形内部的长度”呢?注意到每个圆能转化成这条直线上的一个区间,最后就相当于求几个区间的并。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <cmath>
using namespace std;
#define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
#define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)
 
int read() {
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}
 
#define maxn 1010
 
const double eps = 1e-13;
struct Vec {
    double x, y;
    Vec() {}
    Vec(double _, double __): x(_), y(__) {}
    Vec operator - (const Vec& t) const { return Vec(x - t.x, y - t.y); }
    double len() { return sqrt(x * x + y * y); }
};
struct Circle {
    Vec p; double r;
    Circle() {}
    Circle(double _1, double _2, double _3): p(Vec(_1, _2)), r(_3) {}
} inc[maxn], cs[maxn];
int n;
bool del[maxn];
 
struct Line {
    double l, r;
    Line() {}
    Line(double _, double __): l(_), r(__) {}
    bool operator < (const Line& t) const { return l != t.l ? l < t.l : r > t.r; }
} ls[maxn];
int cl;
 
double calcL(double x) {
    cl = 0;
    rep(i, 1, n) {
        double d = abs(x - cs[i].p.x);
        if(d >= cs[i].r) continue;
        double h = sqrt(cs[i].r * cs[i].r - d * d);
        ls[++cl] = Line(cs[i].p.y - h, cs[i].p.y + h);
    }
    if(!cl) return 0.0;
    sort(ls + 1, ls + cl + 1);
    double nl = ls[1].l, nr = ls[1].r, sum = 0;
    rep(i, 2, cl) if(ls[i].l > nr) {
        sum += nr - nl;
        nl = ls[i].l; nr = ls[i].r;
    }
    else nr = max(nr, ls[i].r);
    return sum + nr - nl;
}
double solve(double l, double r, double lLen, double mLen, double rLen) {
    double mid = (l + r) * 0.5, lmLen = calcL((l + mid) * 0.5), rmLen = calcL((mid + r) * 0.5), S, lS, rS;
    S = (r - l) * (lLen + rLen + 4.0 * mLen) / 6.0;
    lS = (mid - l) * (lLen + mLen + 4.0 * lmLen) / 6.0;
    rS = (r - mid) * (mLen + rLen + 4.0 * rmLen) / 6.0;
    if(abs(lS + rS - S) < eps) return S;
    return solve(l, mid, lLen, lmLen, mLen) + solve(mid, r, mLen, rmLen, rLen);
}
 
int main() {
    double L = 10000, R = -10000;
    n = read();
    rep(i, 1, n) {
        double x = read(), y = read(), r = read();
        inc[i] = Circle(x, y, r);
        L = min(L, x - r); R = max(R, x + r);
    }
    rep(i, 1, n)
        rep(j, 1, n) if(inc[i].r > inc[j].r && (inc[i].p - inc[j].p).len() + inc[j].r <= inc[i].r) del[j] = 1;
    int nn = 0;
    rep(i, 1, n) if(!del[i]) cs[++nn] = inc[i];
    n = nn;
     
    printf("%.3lf\n", solve(L, R, 0, calcL((L + R) * 0.5), 0));
     
    return 0;
}

转载于:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/8542079.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值