【Plantilla】 Unión del área del círculo (lío de Simpson adaptativo)

Título

Primero, la pregunta de la plantilla: SP8073 El área de la unión de círculos

Da algunos círculos en el plano por medio de puntos y radios, y encuentra el área total cubierta por estos círculos.

responder

Similar a esta pregunta es el triángulo P1222 . Sin embargo, debido a algunas propiedades especiales, este problema puede superarse directamente escaneando la línea, y la eficiencia es mucho mayor que la del Simpson adaptativo.

Veamos esta pregunta. Podemos considerar un grupo de círculos que se cruzan en un plano como una función completa, y lo que necesitamos es la suma del área de cada función. Obviamente, se puede transformar en una forma integral, así que me meto con la integral adaptativa de Simpson.

Entonces, ¿cómo pedimos un xxx correspondienteaf (x) f (x)¿Qué pasa con el valor de f ( x ) ? Obviamente estamos enxxCuando se determinax, se requierela línea rectay = xy = xy=x es la longitud total cubierta por el círculo. Entonces podemos enumerar todos los círculos, siempre que la líneay = xy = xy=Para un círculo con puntos de intersección, considere la parte entre los dos puntos de intersección como un segmento de línea, y luego busque con avidez el segmento de línea para cubrir la longitud.

Algunos pequeños detalles: agregar algunos parámetros más al algoritmo adaptativo de Simpson puede reducir los cálculos repetidos. Al mismo tiempo, puede eliminar el círculo pequeño cubierto por un círculo grande antes de comenzar a buscar el área. Luego, para cada pila de círculos conectados a través de la intersección, encuentre los extremos más a la izquierda y más a la derecha, y luego realice la integración adaptativa de Simpson.

Código

#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;
}

Supongo que te gusta

Origin blog.csdn.net/qq_30115697/article/details/90930007
Recomendado
Clasificación