• [学习笔记]自适应辛普森积分


    开计算几何的坑辣

    之前就是一些点、线、面、以及凸包、半平面交、旋转卡壳

    对于面积的并,如果全是矩形,可以矩形面积并,轮廓线全是直线,可以叉积

    当遇到非常不规则的图形组合的时候,如圆弧,就要用到积分了。

    好博客:

    辛普森积分

    思想:不断用二次函数(最常见简单的带弧函数)拟合图像计算面积。

    但是这个拟合,不是直接求出最接近的二次函数,而是用横坐标为l,r,mid三点直接确定二次函数,并且计算积分。

    证明见上面博客。

    显然直接做,,,精度不知道差到哪里去了。

    自适应

    思想:不断用二次函数(最常见简单的带弧函数)拟合图像计算面积。通过分治缩小规模以保证精度。

    通过和l,lmid,mid加上mid,rmid,r的计算面积进行比较,如果误差在允许范围内,直接返回

    否则递归计算左右面积加起来。

    显然规模越小,越精确。

    复杂度:O(玄学)

    适用条件:

    其实比较差劲。

    图像必须数据范围不大,并且精度要求不高。

    而且轮廓线必须“平滑”,出题人不会卡你:

    除去三个“角”,如果那个弧线就是抛物线的话,直接凉凉了。

    例题

    【模板】自适应辛普森法1 套公式。也可以直接手推积分。待定系数法求ln(|ax+b|)的系数

    double a,b,c,d,L,R;
    double f(double x){
        return (c*x+d)/(a*x+b);
    }
    double ji(double a,double b){
        return (b-a)/6*(f(a)+f(b)+4*f((a+b)/2));
    }
    double calc(double l,double r,double eps,double ans){
        double mid=(l+r)/2;
        double le=ji(l,mid),ri=ji(mid,r);
        if(fabs(le+ri-ans)<=eps) return le+ri;
        return calc(l,mid,eps/2,le)+calc(mid,r,eps/2,ri);
    }
    int main(){
        scanf("%lf %lf %lf %lf %lf %lf",&a,&b,&c,&d,&L,&R);
        printf("%.6lf",calc(L,R,1e-7,ji(L,R)));
        return 0;
    }

    【模板】自适应辛普森法2 套公式。大力推导(也可以打表),a<0时候不是收敛的。

    [NOI2005]月下柠檬树 

    难点在求f(x)

    求出相邻圆的公切线(用相似三角形)。求f时候,把所有的圆和线都扫一遍,如果x在其中,则对计算出的纵坐标求max

    直接simpson时候传入f(l),f(r),f(mid)可以从6次计算变成2次计算

    // luogu-judger-enable-o2
    #include<bits/stdc++.h>
    #define reg register int
    #define il inline
    #define fi first
    #define se second
    #define mk(a,b) make_pair(a,b)
    #define numb (ch^'0')
    using namespace std;
    typedef long long ll;
    template<class T>il void rd(T &x){
        char ch;x=0;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');}
    template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');}
    template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('
    ');}
    
    namespace Miracle{
    const int N=505;
    const double Eps=1e-8;
    int n;
    struct cir{
        double x,r;
    }c[N];
    struct po{
        double x,y;
        po(){}
        po(double xx,double yy){
            x=xx;y=yy;
        }
    };
    struct line{
        po L,R;  
    }t[N];
    int tot;
    double gou(double c,double b){
        return sqrt(c*c-b*b);
    }
    void com(int i,int j){
        if(c[i].r>c[j].r){
            if(c[i].x+c[i].r+Eps>c[j].x+c[j].r) return;
            ++tot;
            double L=c[j].x-c[i].x;
            double K=L*c[j].r/(c[i].r-c[j].r);
            double S=K>Eps?c[j].r*c[j].r/K:0;
            double P=gou(c[j].r,S);
            t[tot].R=po(c[j].x+S,P);
            K=K+L;
            S=c[i].r*c[i].r/K;
            P=gou(c[i].r,S);
            t[tot].L=po(c[i].x+S,P);
        }else{
            if(c[j].x-c[j].r-Eps<c[i].x-c[i].r) return;
            ++tot;
            double L=c[j].x-c[i].x;
            double K=L*c[i].r/(c[j].r-c[i].r);
            double S=c[i].r*c[i].r/K;
            double P=gou(c[i].r,S);
            t[tot].L=po(c[i].x-S,P);
            K=K+L;
            S=c[j].r*c[j].r/K;
            P=gou(c[j].r,S);
            t[tot].R=po(c[j].x-S,P);
        }
    }
    double f(double x){
        double ret=0;
        for(reg i=1;i<=n;++i){
            if(c[i].x-c[i].r<=x&&x<=c[i].x+c[i].r){
                double Y=gou(c[i].r,fabs(c[i].x-x));
                if(Y>ret+Eps) ret=Y;
            }
        }
        for(reg i=1;i<=tot;++i){
            if(t[i].L.x<=x&&t[i].R.x>=x){
                double deta=(t[i].R.x-x)*(t[i].R.y-t[i].L.y)/(t[i].R.x-t[i].L.x);
                double Y=t[i].R.y-deta;
                if(Y>ret+Eps) ret=Y;
            }
        }
        return ret;
    }
    double ji(double l,double r,double fl,double fr,double fm){
        return (r-l)/6*(fl+fr+4*fm);
    }
    double calc(double l,double r,double eps,double fl,double fr,double fm,double ans){
        double mid=(l+r)/2;
        double flm=f((l+mid)/2),frm=f((mid+r)/2);
        double le=ji(l,mid,fl,fm,flm),ri=ji(mid,r,fm,fr,frm);
        if(fabs(le+ri-ans)<=15*eps) return le+ri+(le+ri-ans)/15;
        return calc(l,mid,eps/2,fl,fm,flm,le)+calc(mid,r,eps/2,fm,fr,frm,ri);
    }
    int main(){
        rd(n);
        double alp;scanf("%lf",&alp);
        alp=1/tan(alp);
        scanf("%lf",&c[1].x);
        c[1].x*=alp;
        for(reg i=2;i<=n+1;++i){
            scanf("%lf",&c[i].x);
            c[i].x*=alp;
            c[i].x+=c[i-1].x;
        }
        for(reg i=1;i<=n;++i){
            scanf("%lf",&c[i].r);
        }
        ++n;
        c[n].r=0;
        // for(reg i=1;i<=n;++i){
        //     cout<<i<<" : "<<c[i].x<<" "<<c[i].r<<endl;
        // }
        for(reg i=1;i<n;++i){
            com(i,i+1);
        }
        // cout<<" tot "<<tot<<endl;
        // for(reg i=1;i<=tot;++i){
        //     // cout<<" iii "<<i<<endl;
        //     cout<<t[i].L.x<<" "<<t[i].L.y<<endl;
        //     cout<<t[i].R.x<<" "<<t[i].R.y<<endl;
        // }
        double L=23333333,R=-23333333;
        for(reg i=1;i<=n;++i){
            L=min(L,c[i].x-c[i].r);
            R=max(R,c[i].x+c[i].r);
        }
        double fl=f(L),fr=f(R),fm=f((L+R)/2);
        printf("%.2lf",2*calc(L,R,1e-3,fl,fr,fm,ji(L,R,fl,fr,fm)));
        return 0;
    }
    
    }
    signed main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
    */
    View Code
  • 相关阅读:
    JSP内置对象
    Angular $scope和$rootScope事件机制之$emit、$broadcast和$on
    Ionic开发实战
    Entity Framework 5.0 Code First全面学习
    6个强大的AngularJS扩展应用
    使用npm安装一些包失败了的看过来(npm国内镜像介绍)
    自己家里搭建NAS服务器有什么好方案?
    自己动手制作CSharp编译器
    使用Visual Studio Code搭建TypeScript开发环境
    Office web app server2013详细的安装和部署
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10774831.html
Copyright © 2020-2023  润新知