【Template】Union of the area of the circle (adaptive Simpson mess)

Title

First, a template question: SP8073 The area of ​​the union of circles

Give some circles on the plane by means of points and radii, and find the total area covered by these circles.

answer

Similar to this question is the P1222 triangle . However, due to some special properties, this problem can be passed directly by scanning the line, and the efficiency is much higher than that of the adaptive Simpson.

Let's look at this question. We can regard a bunch of intersecting circles on a plane as a whole function, and what we require is the sum of the area of ​​each function. Obviously it can be transformed into an integral form, so I mess with adaptive Simpson integral.

So how do we ask for a xxx corresponding tof (x) f(x)What about the value of f ( x ) ? Obviously we are inxxWhen x is determined, the straight liney = xy = x is requiredY=x is the total length covered by the circle. Then we can enumerate all the circles, as long as the liney = xy = xY=For a circle with intersection points, regard the part between the two intersection points as a line segment, and then greedily find the line segment to cover the length.

Some small details: adding a few more parameters to the adaptive Simpson algorithm can reduce repeated calculations. At the same time, you can delete the small circle covered by a large circle before starting to find the area. Then, for each pile of circles connected through the intersection, find the leftmost and rightmost ends, and then perform adaptive Simpson integration.

Code

#include <bits/stdc++.h>
#define MAX 2005
#define ll long long
#define eps 1e-7
#define INF 1e16
using namespace std;

struct seg{
    
    
    double l, r;
    friend bool operator <(seg a, seg b){
    
    
        if(a.l == b.l) return a.r < b.r;
        return a.l < b.l;
    }
};

struct vec{
    
    
    ll x, y;
};

double dis(vec a, vec b){
    
    
    return sqrt(1.0*(a.x-b.x)*(a.x-b.x) + 1.0*(a.y-b.y)*(a.y-b.y));
}

struct cir{
    
    
    vec o;
    ll r;
    friend bool operator <(cir a, cir b){
    
    
        return a.r < b.r;
    }
}a[MAX];

int n;
vector<seg> v;

int st, ed;
double f(double x){
    
    		//求f(x)
    v.clear();
    for (int i = st; i <= ed; ++i) {
    
    		//处理出交点的纵坐标,保存为线段
        if(x > a[i].o.x+a[i].r || x < a[i].o.x-a[i].r) continue;
        double l = x-a[i].o.x;
        l = sqrt(a[i].r*a[i].r - l*l);
        v.push_back((seg){
    
    a[i].o.y-l, a[i].o.y+l});
    }
    if(v.empty()) return 0.0;
    sort(v.begin(), v.end());		//以下贪心线段覆盖
    double ans = 0, last = v[0].l;
    for (int i = 0; i < v.size(); ++i) {
    
    
        if(v[i].r > last){
    
    
            ans += v[i].r-max(last, v[i].l);
            last = v[i].r;
        }
    }
    return ans;
}

double calc(double l, double r, double sl, double sr){
    
    	//直接计算一段区间的面积
    double mid = (l+r)/2;
    return (r-l)*(sl + 4.0*f(mid) + sr)/6.0;
}

double simpson(double l, double r, double S, double sl, double sr){
    
    
    double mid = (l+r)/2, s = f(mid);
    double t1 = calc(l, mid, sl, s), t2 = calc(mid, r, s, sr);
    if(fabs(t1+t2-S) < eps) return S;
    return simpson(l, mid, t1, sl, s)+simpson(mid, r, t2, s, sr);
}

void init(){
    
    		//预处理,删除被包含的圆
    int cnt = 0;
    for (int i = 1, j; i <= n; ++i) {
    
    
        for (j = i+1; j <= n; ++j) {
    
    
            if(a[i].r-a[j].r + dis(a[i].o, a[j].o) < eps){
    
    
                break;
            }
        }
        if(j > n) a[++cnt] = a[i];
    }
    n = cnt;
}

bool cmp(cir a, cir b){
    
    			//按圆的最左端排序,用于判断连续相交
    return a.o.x-a.r < b.o.x-b.r;
}

double ans;

void solve(){
    
    		//寻找每段联通的圆,进行计算
    double l, r;
    int i = 1, j;
    while(i <= n) {
    
    
        l = a[i].o.x-a[i].r, r = a[i].o.x+a[i].r;
        for (j = i+1; j <= n; ++j) {
    
    
            if(a[j].o.x-a[j].r > r) break;
            r = max(r, (double)a[j].o.x+a[j].r);
        }
        st = i, ed = j-1, i = j;
        double mid = (l+r)/2;
        ans += simpson(l, r, f(mid), f(l), f(r));
    }
}

int main()
{
    
    
    cin >> n;
    double l = INF, r = -INF;
    for (int i = 1; i <= n; ++i) {
    
    
        scanf("%lld%lld%lld", &a[i].o.x, &a[i].o.y, &a[i].r);
        l = min(l, (double)a[i].o.x-a[i].r);
        r = max(r, (double)a[i].o.x+a[i].r);
    }
    sort(a+1, a+n+1);
    init();
    sort(a+1, a+n+1, cmp);
    solve();
    printf("%.3lf\n", ans);

    return 0;
}

Guess you like

Origin blog.csdn.net/qq_30115697/article/details/90930007