自适应辛普森积分

一个完全不会计算几何的蒟蒻的自我拯救……

辛普森积分

有的时候会有一些毒瘤计算几何题,要求的图形面积边缘是一段函数,而这个函数解析式通常非常繁琐,没办法直接用公式积分,所以就需要用辛普森积分求近似值。

辛普森积分的用途就是在精度要求不高的时候(通常是求图形面积)求函数积分的近似值,大概步骤就是在积分区间$[a,b]$中不断二分,每次找$a,b,frac{a+b}{2}$三点用一条抛物线来拟合原函数,当误差小于eps的时候就返回答案。

辛普森积分公式

$$int_{a}^{b}f(x)dxapproxfrac{b-a}{6} imesleft[f(a)+f(b)+4fleft(frac{a+b}{2} ight) ight]$$

下面推导一波:

设$g(x)$是一个关于$x$的二次函数,且$g(x)=Ax^2+Bx+C$,对于定积分$int_{0}^{a}g(x)dx$求积得$frac{Ax^3}{3}+frac{Bx^2}{2}+Cx+D$,其中$D$为常数可以忽略;

令$H(x)=int_{0}^{a}g(x)dx$,则求其中的一段定积分就有$int_{a}^{b}g(x)dx=H(b)-H(a)$;

设$g(x)$是当前的拟合函数,那么有:

$$int_{a}^{b}f(x)dxapproxint_{a}^{b}g(x)dx$$

$$=H(b)-H(a)$$

$$=frac{A}{3}(b^3-a^3)+frac{B}{2}(b^2-a^2)+C(b-a)$$

$$=frac{b-a}{6} imesleft[2A(a^2+ab+b^2)+3B(a+b)+6C ight]$$

然后大力拆括号配方:

$$=frac{b-a}{6} imesleft[(Aa^2+Ba+C)+(Ab^2+Bb+C)+A(a^2+2ab+b^2)+2B(a+b)+4C ight]$$

$$=frac{b-a}{6} imesleft[g(a)+g(b)+4Aleft(frac{a+b}{2} ight)^2+4Bleft(frac{a+b}{2} ight)+4C ight]$$

$$=frac{b-a}{6} imesleft[g(a)+g(b)+4gleft(frac{a+b}{2} ight) ight]$$

在实际使用的时候,$g(x)$拟合的曲线可以用$f(x)$代替,因此:

$$int_{a}^{b}f(x)dxapproxfrac{b-a}{6} imesleft[f(a)+f(b)+4fleft(frac{a+b}{2} ight) ight]$$

实现:

具体实现就是不断二分区间,用辛普森积分公式求出近似值,当精度达到要求后就返回;

写起来很方便,核心代码就十几行左右……

注意用辛普森的时候要大力调eps,否则可能会在WA和TLE之间徘徊……

代码:

模板:洛咕P4525 自适应辛普森法1

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<cmath>
 6 #include<queue>
 7 #define inf 2147483647
 8 #define eps 1e-9
 9 using namespace std;
10 typedef long long ll;
11 double a,b,c,d,l,r;
12 double f(double x){
13     return (c*x+d)/(a*x+b);
14 }
15 double simpson(double l,double r){
16     return (f(l)+f(r)+4*f((l+r)/2))*(r-l)/6;
17 }
18 double sint(double l,double r,double v){
19     double mid=(l+r)/2,lv=simpson(l,mid),rv=simpson(mid,r);
20     if(fabs(lv+rv-v)<=eps){
21         return v;
22     }
23     return sint(l,mid,lv)+sint(mid,r,rv);
24 }
25 double getint(double l,double r){
26     return sint(l,r,simpson(l,r));
27 }
28 int main(){
29     scanf("%lf%lf%lf%lf%lf%lf",&a,&b,&c,&d,&l,&r);
30     printf("%.6lf",getint(l,r));
31     return 0;
32 }

一个精度更高的做法:

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<cmath>
 6 #include<queue>
 7 #define inf 2147483647
 8 //#define eps 1e-7
 9 using namespace std;
10 typedef long long ll;
11 const double eps=1e-7;
12 double a,b,c,d,l,r;
13 double f(double x){
14     return (c*x+d)/(a*x+b);
15 }
16 double simpson(double l,double r){
17     return (f(l)+f(r)+4*f((l+r)/2))*(r-l)/6;
18 }
19 double sint(double l,double r,double ep,double v){
20     double mid=(l+r)/2,lv=simpson(l,mid),rv=simpson(mid,r);
21     if(fabs(lv+rv-v)<=ep*15){
22         return lv+rv+(lv+rv-v)/15;
23     }
24     return sint(l,mid,ep/2,lv)+sint(mid,r,ep/2,rv);
25 }
26 double getint(double l,double r,double ep){
27     return sint(l,r,ep,simpson(l,r));
28 }
29 int main(){
30     scanf("%lf%lf%lf%lf%lf%lf",&a,&b,&c,&d,&l,&r);
31     printf("%.6lf",getint(l,r,eps));
32     return 0;
33 }

例题

【BZOJ1502】【NOI2005】月下柠檬树

若干个圆台投影之后形状大概是若干个圆加外公切线围成的图形,然后最顶上是个圆锥,整个图形是轴对称的,大力辛普森积分就好了;

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<cmath>
 6 #include<queue>
 7 #define inf 2147483647
 8 #define eps 1e-9
 9 using namespace std;
10 typedef long long ll;
11 typedef double db;
12 struct cir{
13     db x,y,xx,yy;
14 }a[1001];
15 int n,tot=0;
16 db al,mi,mx,t,h[1001],s[1001],r[1001];
17 db f(db x){
18     db ret=0;
19     for(int i=0;i<=n;i++){
20         if(fabs(s[i]-x)<r[i]){
21             ret=max(ret,sqrt(r[i]*r[i]-(s[i]-x)*(s[i]-x)));
22         }
23     }
24     for(int i=1;i<=tot;i++){
25         if(x>a[i].x&&x<a[i].xx){
26             ret=max(ret,a[i].y+(a[i].yy-a[i].y)*(x-a[i].x)/(a[i].xx-a[i].x));
27         }
28     }
29     return ret;
30 }
31 db sim(db l,db r,db fl,db fm,db fr){
32     return (fl+fr+4*fm)*(r-l)/6;
33 }
34 db sint(db l,db r,db fl,db fm,db fr){
35     db mid=(l+r)/2,L=f((l+mid)/2),R=f((mid+r)/2),s=sim(l,r,fl,fm,fr),sl=sim(l,mid,fl,L,fm),sr=sim(mid,r,fm,R,fr);
36     if(fabs(sl+sr-s)<eps)return sl+sr;
37     return sint(l,mid,fl,L,fm)+sint(mid,r,fm,R,fr);
38 }
39 int main(){
40     scanf("%d%lf",&n,&al);
41     for(int i=0;i<=n;i++){
42         scanf("%lf",&h[i]);
43         if(i)h[i]+=h[i-1];
44         s[i]=h[i]/tan(al);
45     }
46     for(int i=0;i<n;i++){
47         scanf("%lf",&r[i]);
48         mi=min(mi,s[i]-r[i]);
49         mx=max(mx,s[i]+r[i]);
50     }
51     mx=max(mx,s[n]);
52     for(int i=0;i<n;i++){
53         t=s[i+1]-s[i];
54         if(t>fabs(r[i+1]-r[i])){
55             a[++tot].x=s[i]-r[i]*(r[i+1]-r[i])/t;
56             a[tot].y=sqrt(r[i]*r[i]-(a[tot].x-s[i])*(a[tot].x-s[i]));
57             a[tot].xx=s[i+1]-r[i+1]*(r[i+1]-r[i])/t;
58             a[tot].yy=sqrt(r[i+1]*r[i+1]-(a[tot].xx-s[i+1])*(a[tot].xx-s[i+1]));
59         }
60     }
61     printf("%.2lf",sint(mi,mx,0,f((mi+mx)/2),0)*2);
62     return 0;
63 }

【BZOJ2178】圆的面积并

先把被包含的圆去掉,然后大力切圆就好了

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<cmath>
 6 #include<queue>
 7 #define inf 2147483647
 8 #define eps 1e-9
 9 using namespace std;
10 typedef long long ll;
11 typedef double db;
12 struct node{
13     db x,y;
14     node(){}
15     node(db _x,db _y){x=_x,y=_y;}
16     friend bool operator <(node a,node b){
17         return (fabs(a.x-b.x)<eps)?a.y<b.y:a.x<b.x;
18     }
19 }p[1001];
20 struct cir{
21     db x,y,r;
22     bool ch;
23     friend bool operator <(cir a,cir b){
24         return a.x<b.x;
25     }
26     node getf(db xx){
27         if(fabs(x-xx)>=r)return node(0,0);
28         return node(y-sqrt(r*r-(x-xx)*(x-xx)),y+sqrt(r*r-(x-xx)*(x-xx)));
29     }
30 }c[1001];
31 int n;
32 db mi=inf,mx=-inf;
33 db dis(cir a,cir b){
34     return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
35 }
36 db f(db x){
37     db ret=0,ls=-inf;
38     int top=0;
39     for(int i=1;i<=n;i++){
40         p[++top]=c[i].getf(x);
41         if(p[top].x==0&&p[top].y==0)top--;
42     }
43     sort(p+1,p+top+1);
44     for(int i=1;i<=top;i++){
45         if(p[i].x>ls){
46             ret+=p[i].y-p[i].x;
47             ls=p[i].y;
48         }else if(p[i].y>ls){
49             ret+=p[i].y-ls;
50             ls=p[i].y;
51         }
52     }
53     return ret;
54 }
55 db sim(db l,db r,db fl,db fm,db fr){
56     return (fl+4*fm+fr)*(r-l)/6;
57 }
58 db sint(db l,db r,db fl,db fm,db fr){
59     db mid=(l+r)/2,L=f((l+mid)/2),R=f((mid+r)/2),s=sim(l,r,fl,fm,fr),sl=sim(l,mid,fl,L,fm),sr=sim(mid,r,fm,R,fr);
60     if(fabs(sl+sr-s)<eps)return sl+sr;
61     return sint(l,mid,fl,L,fm)+sint(mid,r,fm,R,fr);
62 }
63 int main(){
64     scanf("%d",&n);
65     for(int i=1;i<=n;i++){
66         scanf("%lf%lf%lf",&c[i].x,&c[i].y,&c[i].r);
67         mi=min(mi,c[i].x-c[i].r);
68         mx=max(mx,c[i].x+c[i].r);
69     }
70     sort(c+1,c+n+1);
71     for(int i=1;i<=n;i++){
72         if(!c[i].ch){
73             for(int j=i+1;j<=n;j++){
74                 if(!c[j].ch&&dis(c[i],c[j])<=c[i].r-c[j].r){
75                     c[j].ch=true;
76                 }
77             }
78         }
79     }
80     for(int i=1;i<=n;i++){
81         if(c[i].ch)swap(c[i--],c[n--]);
82     }
83     printf("%.3lf",sint(mi,mx,0,f((mi+mx)/2),0));
84     return 0;
85 }
原文地址:https://www.cnblogs.com/dcdcbigbig/p/10056681.html