自适应Simpson积分

首先介绍一下Simpson积分的公式$$\int_{a}^{b}f(x)dx \approx \frac {b-a} {6} (f(a)+4f(\frac{a+b} {2})+f(b))$$

可以用这个求比较平滑的曲线的积分的近似...

但是直接用这个公式来做会误差很大

具体用法是:

用这个方法积$[l,mid]$,积$[mid,r]$,再积$[l,r]$判断是不是在误差范围内

如果不在就递归地细化$[l,mid]$和$[mid,r]$

比如说SDOI2017龙与地下城这道题

就用这种方法积了一个正态分布曲线

#include<bits/stdc++.h>

using namespace std;

inline int read()
{
    int x=0;char ch=getchar();
    while(ch<'0' || '9'<ch)ch=getchar();
    while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
    return x;
}

typedef double db;

db x,y;

namespace Fast_Fouriet_Transform
{
    typedef complex<db> cp;
    const int M=800009;
    const db pi=acos(-1);

    cp a[M],b[M];
    db per,ans[M];
    int m,l,rev[M];

    inline void FFT(cp *a,int n,int f)
    {
        for(int i=0;i<n;i++)
            if(i<rev[i])
                swap(a[i],a[rev[i]]);
        for(int h=2;h<=n;h<<=1)
        {
            cp w(cos(2*pi/h),f*sin(2*pi/h));
            for(int j=0;j<n;j+=h)
            {
                cp wn(1,0),x,y;
                for(int k=j;k<j+(h>>1);k++)
                {
                    x=a[k],y=wn*a[k+(h>>1)];
                    a[k]=x+y;
                    a[k+(h>>1)]=x-y;
                    wn*=w;
                }
            }
        }
        if(f==-1)
            for(int i=0;i<n;i++)
                a[i].real()/=(db)n;
    }

    int mian()
    {
        per=1.0/(db)x;

        for(m=1,l=0;m<(y*x);m<<=1)l++;
        for(int i=0;i<m;i++)
            rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));

        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));

        a[0].real()=1.0;
        for(int i=0;i<x;i++)
            b[i].real()=per;

        FFT(a,m,1);FFT(b,m,1);
        for(int i=0;i<m;i++)
        {
            int cnt=y;
            while(cnt)
            {
                if(cnt&1)
                    a[i]=a[i]*b[i];
                b[i]=b[i]*b[i];
                cnt>>=1;
            }
        }
        FFT(a,m,-1);

        ans[0]=a[0].real();
        for(int i=1;i<m;i++)
            ans[i]=a[i].real()+ans[i-1];

        for(int i=1;i<=10;i++)
        {
            int a=read(),b=read();
            if(a==0)
                printf("%.8lf\n",ans[b]);
            else
                printf("%.8lf\n",ans[b]-ans[a-1]);
        }
        return 0;
    }
}

namespace simpsons
{
    const db pi2=sqrt(acos(-1)*2.0);
    const db eps=1e-12;
    db sigma,mu;
    inline db sqr(db x){return x*x;}
    inline db f(db x){return exp(-sqr(x)/2)/pi2;}
    inline bool dcmp(db x){return fabs(x)<15*eps;}
    inline void init()
    {
        sigma=sqrt((sqr(x)-1.0)/12.0);
        mu=(x-1.0)/2.0;
    }

    inline db simpson(db a,db b,db fl,db fmid,db fr)
    {
        return (b-a)*(fl+4.0*fmid+fr)/6.0;
    }

    inline db solve(db l,db r)
    {
        db mid=(l+r)/2.0,lm=(l+mid)/2.0,mr=(mid+r)/2.0;
        db fl=f(l),fmid=f(mid),fr=f(r),flm=f(lm),fmr=f(mr);

        db sipl=simpson(l,mid,fl,flm,fmid);
        db sipr=simpson(mid,r,fmid,fmr,fr);
        db sipm=simpson(l,r,fl,fmid,fr);

        if(dcmp(sipl+sipr-sipm))
            return sipl+sipr+(sipl+sipr-sipm)/15;
        else return solve(l,mid)+solve(mid,r);
    }

    int mian()
    {
        init();
        db base=solve(0,(x-1)*y);
        for(int i=1;i<=10;i++)
        {
            db a=(((db)read()-0.5)/y-mu)*sqrt(y)/sigma;
            db b=(((db)read()+0.5)/y-mu)*sqrt(y)/sigma;
            printf("%.8lf\n",solve(0,b)-solve(0,a));
        }
    }
}

int main()
{
    int T=read();
    while(T--)
    {
        scanf("%lf%lf",&x,&y);
        if(x*y<=400000)
            Fast_Fouriet_Transform::mian();
        else
            simpsons::mian();
    }

    return 0;
}
View Code

还有经典的NOI2005月下柠檬树

#include<iostream> 
#include<cstdio> 
#include<cstring> 
#include<algorithm> 
#include<cmath> 
using namespace std; 
#define MAXN 1010 
double alpha; 
int N,num; 
#define INF 1e12 
#define eps 1e-5 
struct Point 
{ 
    double x,y; 
    Point (double X=0,double Y=0) {x=X; y=Y;} 
}; 
struct Circle 
{ 
    double r; 
    Point c; 
    Circle(Point C=(Point){0,0},double R=0) {c=C; r=R;} 
}C[MAXN]; 
struct Line 
{ 
    Point s,t; 
    double k,b; 
    Line(Point S=(Point){0,0},Point T=(Point){0,0})  
        { 
            s=S,t=T; 
            if (s.x>t.x) swap(s,t); 
            k=(s.y-t.y)/(s.x-t.x); 
            b=s.y-k*s.x; 
        } 
    double f(double x) {return k*x+b;} 
}l[MAXN]; 
int dcmp(double x) {if (fabs(x)<eps) return 0; return x<0? -1:1;} 
double F(double x) 
{ 
    double re=0; 
    for (int i=1; i<=N; i++) //枚举圆是否与扫描线有交
        { 
            double d=fabs(x-C[i].c.x); 
            if (dcmp(d-C[i].r)>0) continue; 
            double len=2*sqrt(C[i].r*C[i].r-d*d); 
            re=max(re,len);  
        } 
    for (int i=1; i<=num; i++) //枚举公切线
        if (x>=l[i].s.x && x<=l[i].t.x) re=max(re,2*l[i].f(x)); 
    return re; 
} //利用扫描线去判断
double Calc(double l,double r) {double mid=(l+r)/2; return (F(l)+F(r)+F(mid)*4)*(r-l)/6;} 
double Simpson(double l,double r,double now) 
{ 
    double mid=(l+r)/2; 
    double x=Calc(l,mid),y=Calc(mid,r); 
    if (!dcmp(now-x-y)) return now; 
    else return Simpson(l,mid,x)+Simpson(mid,r,y); 
} 
void Solve() 
{ 
    double L=INF,R=-INF; 
    for (int i=1; i<=N+1; i++) 
        L=min(L,C[i].c.x-C[i].r),R=max(R,C[i].c.x+C[i].r); 
//  printf("%lf\n%lf\n",L,R); 
    for (int i=1; i<=N; i++) 
        { 
            double d=C[i+1].c.x-C[i].c.x;  
            if (dcmp(d-fabs(C[i].r-C[i+1].r))<0) continue; //特判小圆被大圆覆盖的情况
            double sina=(C[i].r-C[i+1].r)/d,cosa=sqrt(1-sina*sina); 
            l[++num]=(Line){(Point){C[i].c.x+C[i].r*sina,C[i].r*cosa},(Point){C[i+1].c.x+C[i+1].r*sina,C[i+1].r*cosa}}; 
        } 
    printf("%.2lf\n",Simpson(L,R,Calc(L,R))); 
} 
int main() 
{ 
    scanf("%d%lf",&N,&alpha); 
    double h,r; 
    for (int i=1; i<=N+1; i++) 
        scanf("%lf",&h), 
        C[i]=(Circle){((Point){(h/tan(alpha))+C[i-1].c.x,0}),0}; 
    for (int i=1; i<=N; i++) 
        scanf("%lf",&r),C[i].r=r; 
//  for (int i=1; i<=N+1; i++) 
//      printf("%d     %.2lf     %.2lf\n",i,C[i].c.x,C[i].r); 
    Solve(); 
    return 0; 
}
View Code

感觉...算个黑科技吧

攒着以后可能会有点小用

posted @ 2018-02-21 13:53  探险家Mr.H  阅读(201)  评论(0编辑  收藏  举报