题解 P4525 【【模板】自适应辛普森法1】

FZzzz

2019-07-14 12:34:03

Solution

## 写在前面 本题解包含两部分: 1. 一个我看起来比较舒服的数学推导 1. 自适应辛普森的一个简单~~并且没什么卵用~~的优化 ## 数学推导 看好多大佬都用数学方法做的,但是我这个垃圾并看不懂。所以这里给出一个好懂一点的优化。 记被积函数为$f(x)$。 首先,我们谈判$a=0$的情况。 这种情况比较简单,我就不说了。(逃 若$a\not=0$,将被积函数进行代数变形 $$\begin{aligned}f(x)&=\frac{cx+d}{ax+b}\\&=\frac{c}{a}+\frac{\frac{ad-bc}{a}}{ax+b}\end{aligned}$$ 则 $$\int f(x)\mathrm{d}x=\int\frac{c}{a}\mathrm{d}x+\frac{ad-bc}{a}\int\frac{\mathrm{d}x}{ax+b}$$ 加号左边那个积分是很容易积出来的,而查一下积分表,有 $$\int\frac{\mathrm{d}x}{ax+b}=\frac{1}{a}\ln|ax+b|+C$$ 故 $$\int f(x)\mathrm{d}x=\frac{ad-bc}{a^2}\ln|ax+b|+\frac{cx}{a}+C$$ 于是就可以积出题目中那个式子了。代码如下: ```cpp #include<cmath> #include<cstdio> double a,b,c,d; double F(double x){ return (a*d-b*c)*log(fabs(a*x+b))/(a*a)+c*x/a; } double F2(double x){ return c*x*x/(2*b)+d*x/b; } int main(){ #ifdef LOCAL freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif double l,r; scanf("%lf%lf%lf%lf%lf%lf",&a,&b,&c,&d,&l,&r); if(a!=0.0) printf("%.6f\n",F(r)-F(l)); else printf("%.6f\n",F2(r)-F2(l)); return 0; } ``` ## 自适应辛普森 普通的自适应辛普森我就不多说了,代码如下: ```cpp #include<cmath> #include<cstdio> double a,b,c,d; double f(double x){ return (c*x+d)/(a*x+b); } //区间[a,b]上的辛普森值 double simpson(double a,double b){ double c=a+(b-a)/2; return (f(a)+4*f(c)+f(b))*(b-a)/6; } //区间[a,b]上的积分,精度限制为eps,已知整个区间的辛普森值A double asr(double a,double b,double eps,double A){ double c=a+(b-a)/2; double L=simpson(a,c),R=simpson(c,b); if(fabs(L+R-A)<=15*eps) return L+R+(L+R-A)/15; else return asr(a,c,eps/2,L)+asr(c,b,eps/2,R); } const double eps=1e-7; int main(){ #ifdef LOCAL freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif double l,r; scanf("%lf%lf%lf%lf%lf%lf",&a,&b,&c,&d,&l,&r); printf("%.6f\n",asr(l,r,eps,simpson(l,r))); return 0; } ``` 然后我翻了一下题解,竟然所有大佬都是这么写的。 仔细看一下这份代码,我们发现,有很多函数值被重复计算了很多次。在本题中,这是不要紧的。但在一些题中,函数值不能用常数时间计算。所以,我们需要维护算过的函数值。代码如下: ```cpp #include<cmath> #include<cstdio> double a,b,c,d; double f(double x){ return (c*x+d)/(a*x+b); } //区间[a,b]上的辛普森值,已知区间长度l和端点及中点处的函数值A,B,C double simpson(double l,double A,double B,double C){ return (A+4*C+B)*l/6; } double asr(double a,double b,double eps,double A,double B,double C){ double l=b-a; double c=a+l/2; double D=f(a+l/4),E=f(a+3*l/4); double L=simpson(l/2,A,C,D),R=simpson(l/2,C,B,E),AB=simpson(l,A,B,C); if(fabs(L+R-AB)<=15*eps) return L+R+(L+R-AB)/15; else return asr(a,c,eps/2,A,C,D)+asr(c,b,eps/2,C,B,E); } const double eps=1e-7; int main(){ #ifdef LOCAL freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif double l,r; scanf("%lf%lf%lf%lf%lf%lf",&a,&b,&c,&d,&l,&r); printf("%.6f\n",asr(l,r,eps,f(l),f(r),f(l+(r-l)/2))); return 0; } ``` 我知道这种码风很难看,但是我找不到好看的了…… ------------ 完结撒花!~~顺便骗赞~~