[Template] Unification de l'aire du cercle (mess Simpson adaptatif)

Titre

Tout d'abord, une question modèle: SP8073 La zone de l'union des cercles

Donnez quelques cercles sur le plan au moyen de points et de rayons, et trouvez la surface totale couverte par ces cercles.

répondre

Le triangle P1222 est similaire à cette question . Cependant, en raison de certaines propriétés spéciales, ce problème peut être résolu directement en scannant la ligne, et l'efficacité est beaucoup plus élevée que celle du Simpson adaptatif.

Regardons cette question. Nous pouvons considérer un ensemble de cercles qui se croisent sur un plan comme une fonction entière, ce dont nous avons besoin, c'est la somme de l'aire de chaque fonction. Évidemment, il peut être transformé en une forme intégrale, donc je joue avec l'intégrale adaptative de Simpson.

Alors, comment demander un xxx correspondant àf (x) f (x)Qu'en est-il de la valeur de f ( x ) ? Evidemment nous sommes enxxLorsque x est déterminé, la droitey = xy = x est requiseOui=x est la longueur totale couverte par le cercle. Ensuite, nous pouvons énumérer tous les cercles, tant que la ligney = xy = xOui=Pour un cercle avec des points d'intersection, considérez la partie entre les deux points d'intersection comme un segment de ligne, puis trouvez goulûment le segment de ligne pour couvrir la longueur.

Quelques petits détails: l'ajout de quelques paramètres supplémentaires à l'algorithme adaptatif Simpson peut réduire les calculs répétés. Dans le même temps, vous pouvez supprimer le petit cercle couvert par un grand cercle avant de commencer à trouver la zone. Ensuite, pour chaque pile de cercles connectés via l'intersection, recherchez les extrémités les plus à gauche et à droite, puis effectuez une intégration Simpson adaptative.

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

Je suppose que tu aimes

Origine blog.csdn.net/qq_30115697/article/details/90930007
conseillé
Classement