自适应 Simpson 积分法,是一种计算一段区间内,形态奇怪的函数和的算法,例如面积并和难以直接用通项公式计算的函数。
Simpson 积分
我们都知道,求解微积分需要求解一个导数的原函数,但这显然更难,比如说 \(f(x)=\dfrac{cx+d}{ax+b}\) 这个函数,就相当难以求解原函数 \(F(x)\) (对应的例题是这个)。
假如我们在求定积分,我们考虑使用一个与 \(f(x)\) 函数图像相近的函数 \(f_n(x)=\sum\limits_{i=0}^na_ix^i\)。显然,\(F_n(x)\) 比较好求,为 \(\alpha+\sum\limits_{i=0}^na'_ix^{i+1}\)。那么我们就得到:
为了方便运算,我们只考虑 \(n=2\) 的情况。相当于此时 \(f_2(x)\) 是一个二次函数。众所周知,要确定一个二次函数,需要确定其上的三个点。我们考虑选择 \(a,b,c=\dfrac{a+b}2\) 三点。我们设 \(f_2(x)=Ax^2+Bx+C\),则 \(F_2=\dfrac A3x^3+\dfrac B2x^2+Cx+\alpha\)。于是有:
于是我们就得到了 Simpson 积分的一个公式:
自适应 Simpson 积分法
上面这个式子很不错,但是不够好,因为他太粗略了。
比如 \(\int_{-1}^1\frac{3x+4}{x+2}dx\),用 Simpson 积分算出来的值为 \(3.77\cdots\),而实际值已经超过 \(3.8\) 了。这就错的相当离谱。
其根本原因就在于,我们选择的 \(f_2(x)\) 不像 \(f(x)\)。但是我们还有一招:
为了方便,我们设 \(c=\frac{a+b}2\)。于是想到将原区间分成大量小区间,再用 Simpson 积分求出每个小区间的值,最后再全部合并起来。
比如上述问题,我们将问题转化为:
假如肯再分解一层,就会得到 \(3.802549\cdots\),已经相当接近正确答案 \(3.802775\cdots\) 了。
但是显然,继续再分解下去一定会造成 \(TLE\),所以当务之急是确定递归的边界条件。
想法也很简单,假如满足:
那么就停止递归,否则继续递归,同时 \(eps=\frac{eps}2\)。这里 \(\alpha\) 是一个常量,基本上可以说卡常专用了。
#include<bits/stdc++.h>
#define fsc(x) fixed<<setprecision(x)
using namespace std;
double a,b,c,d,L,R;
double f(double x){return (c*x+d)/(a*x+b);
}double simpson(double l,double r){double mid=(l+r)/2;return (f(l)+f(mid)*4+f(r))*(r-l)/6;
}double dfs(double l,double r,double as,double eps){double mid=(l+r)/2;double a0=simpson(l,mid),a1=simpson(mid,r);if(fabs(a0+a1-as)<=eps*15)return a0+a1+(a0+a1-as)/15;return dfs(l,mid,a0,eps/2)+dfs(mid,r,a1,eps/2);
}int main(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);cin>>a>>b>>c>>d>>L>>R;cout<<fsc(6)<<dfs(L,R,simpson(L,R),1e-6);return 0;
}
面积并
众所周知,有一道《计算几何水题》,他叫月下柠檬树。这里将以这道题讲解面积并问题如何转化为 Simpson 积分。
计算几何部分,考虑圆的投影还是一个等大的圆,只需要确定圆心就可以了。
圆台有两种情况。假如一个大圆完全包含另一个小圆,那么圆台的投影就是大圆;否则两圆必有两条公切线,连接后形成的图形即为圆台投影。
转化为公切线两端点,向各自圆心连线,加上公切线和两圆心连线所围成的图形(实为直角梯形)与两圆的面积并。
圆形部分好求,用相似三角形和勾股定理可以简单求出直角梯形的四个交点。
为减少时间复杂度,我们将圆形转为半圆,同时每个圆台只保留一个梯形,最终答案 \(\times 2\) 即可。
注:我求面积并的方法比较非主流,不喜勿喷。
那我们考虑设 \(f(x_0)\) 表示对于每个图形,和 \(x=x_0\) 的两个交点连成的线段的线段并。假如没有两个交点,那么我们就不管它。
有一个基本的思路是,面积并等于大量线段并之和,所以我们这样定义。
将梯形转化为更好求解的三角形,再进行求解即可。
#include<bits/stdc++.h>
#include<bits/extc++.h>
#define fsc(x) fixed<<setprecision(x)
#define SZ 5056577
using namespace std;
const int N=1505;
int n,m,k;double rd[N],o[N];
double al,sc[N][3][2],L,R;
struct hash_map{struct data{double v;int u,nex;};data e[SZ<<1];int h[SZ],cnt;double& operator[](int u){int hu=u%SZ;for(int i=h[hu];i;i=e[i].nex) if(e[i].u==u) return e[i].v;return e[++cnt]=(data){0.0,u,h[hu]},h[hu]=cnt,e[cnt].v;}
}mp;struct line{double l,r;}ans[N];
int cmp(line x,line y){return x.l<y.l;}
double dts(double *a,double *b,double x){return a[1]+(a[1]-b[1])/(a[0]-b[0])*(x-a[0]);
}double f(double x){if(mp[(int)(x*500)]) return mp[(int)(x*500)];for(int i=1;i<=n;i++){if(x<o[i]-rd[i]||x>o[i]+rd[i]) continue;ans[++k].l=0;ans[k].r=sqrt(rd[i]*rd[i]-pow(x-o[i],2));}for(int i=1;i<=m;i++){if(x<sc[i][0][0]||x>sc[i][2][0]) continue;ans[++k].l=dts(sc[i][0],sc[i][2],x);ans[k].r=dts(sc[i][1],sc[i][2*(sc[i][1][0]<x)],x);if(ans[k].l>ans[k].r) swap(ans[k].l,ans[k].r);}sort(ans+1,ans+k+1,cmp);double re=0,lst=-1e9;for(int i=1;i<=k;lst=max(lst,ans[i++].r))re+=max(0.0,ans[i].r-max(lst,ans[i].l));return k=0,mp[(int)(x*500)]=re*2;
}double simpson(double l,double r){return (f(l)+4*f((l+r)/2)+f(r))*(r-l)/6;
}double dfs(double l,double r,double as,double eps){double mid=(l+r)/2;double a0=simpson(l,mid),a1=simpson(mid,r);if(fabs(a0+a1-as)<=eps*15) return a0+a1+(a0+a1-as)/15;return dfs(l,mid,a0,eps/2)+dfs(mid,r,a1,eps/2);
}signed main(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);cin>>n>>al,L=1e9,R=-1e9;for(int i=1;i<=n+1;i++) cin>>o[i],o[i]+=o[i-1];for(int i=1;i<=n;i++){o[i]/=tan(al),cin>>rd[i];L=min(o[i]-rd[i],L);R=max(o[i]+rd[i],R);}o[n+1]/=tan(al);L=min(o[n+1],L),R=max(o[n+1],R);for(int i=1;i<=n;i++){if(o[i]+rd[i]>=o[i+1]+rd[i+1]) continue;if(o[i]-rd[i]>=o[i+1]-rd[i+1]) continue;sc[++m][1][0]=o[i+1],sc[m+1][0][0]=sc[m][0][0]=o[i];sc[m+1][2][0]=sc[m][2][0]=o[i+1]+(rd[i]-rd[i+1])*rd[i+1]/(o[i+1]-o[i]);sc[m+1][2][1]=sc[m][2][1]=sqrt(rd[i+1]*rd[i+1]-pow(sc[m][2][0]-o[i+1],2));sc[++m][1][0]=o[i]+(rd[i]-rd[i+1])*rd[i]/(o[i+1]-o[i]);sc[m][1][1]=sqrt(rd[i]*rd[i]-pow(sc[m][1][0]-o[i],2));}for(int i=1;i<=m;i++){if(sc[i][0][0]>sc[i][1][0])swap(sc[i][0],sc[i][1]);if(sc[i][1][0]>sc[i][2][0])swap(sc[i][1],sc[i][2]);if(sc[i][0][0]>sc[i][1][0])swap(sc[i][0],sc[i][1]);}cout<<fsc(2)<<dfs(L,R,simpson(L,R),2e-3);return 0;
}