1558. 超椭圆 II

本题有多种解法。仅展示其中几种。不代表题解解法是最佳解法,欢迎评论区补充 qwq

以下参考代码暂仅提供 C++ 代码。

方法一:蒙特卡罗法

即随机生成足够多的范围内的随机点,判断这个点是否在方程的内部,如果在就是面积的一部分。

注意到本题的方程关于x,y轴对称,为了减少计算负数带来的误差,我们可以只生成第一象限的随机点。(方法二也用到了对称性)

参考代码如下:

#include <bits/stdc++.h> using namespace std; const int rands = 10000000; int cor, n, a, b; double am, bm; void mtkl() { double rx = rand() / am / a, ry = rand() / bm / b; double ax = 1, ay = 1; for (int i = 0; i < n; ++i) //n比较小不开快速幂也可以 ax *= rx, ay *= ry; if (ax + ay < 1) ++cor; } signed main() { scanf("%d%d%d", &n, &a, &b); srand(time(nullptr)); am = 1. * RAND_MAX / a; bm = 1. * RAND_MAX / b; for (int i = 0; i < rands; ++i) mtkl(); printf("%lf", 4. * cor * a * b / rands); return 0; }

 

 

方法二:微分法

在范围内等距离划分为许多个密集的小点,判断这些小点是否在方程内。

在本题中,在同等的时间开销下,微分法的精度不如蒙特卡罗法。

为了加速计算,可以使用快速幂。

参考代码如下:

#include <bits/stdc++.h> int a, b, n, cor, tot; double as, ae, bs, be, step = 1e-2; signed main() { scanf("%d%d%d", &n, &a, &b); as = -a, bs = -b, ae = a, be = b; for (double ai = 0; ai < ae; ai += step) for (double bi = 0; bi < be; bi += step) { double x = ai / a, y = bi / b, ax = 1, ay = 1; for (int i = n; i; i >>= 1, x *= x, y *= y) //快速幂 if (i & 1) ax *= x, ay *= y; if (ax + ay < 1) ++cor; ++tot; } printf("%lf", 4. * a * b * cor / tot); return 0; }

 

 

方法三:微分法(二分优化)

我们发现,这道题可以直接寻找图形的边界,寻找边界是符合单调性的,所以在微分的过程中,其中一个维度可以使用二分优化。那么每一个长条的小面积可以近似为一个矩形的面积。(在方法二的微分法中是用点来微分,在这里是用小长方形来微分)。

参考代码如下:

#include <bits/stdc++.h> using namespace std; typedef double db; int n, a, b; db step = 1e-4, s; inline bool cal(db x, db y) { db vx = x / a, vy = y / b, rx = 1, ry = 1; for (int i = n; i; i >>= 1, vx *= vx, vy *= vy) if (i & 1) rx *= vx, ry *= vy; return rx + ry < 1; } signed main() { scanf("%d%d%d", &n, &a, &b); for (db bs = 0.0; bs <= b; bs += step) { db left = 0.0, right = a, mid; while (right - left > 1e-4) //找边界 { mid = (left + right) / 2; bool v = cal(mid, bs); if (v) //点在超椭圆内 left = mid + 1e-5; else right = mid - 1e-5; } s += mid * step; } printf("%lf", 4 * s); return 0; }

 

 

方法四:积分法

依然是因为对称,所以只需要计算第一象限。对原方程作变换,得: y=b\times^n\sqrt{1-(\frac{x}{a})^n} 积分得,第一象限的面积表达式为: S=b\int_0^a\ ^n\sqrt{1-(\frac{x}{a})^n} 利用积分的定义法(计算黎曼和,大量小面积),很容易得到答案 qwq。

参考代码:

#include <bits/stdc++.h> using namespace std; typedef double db; db n, a, b, step = 1e-4, s; signed main() { scanf("%lf%lf%lf", &n, &a, &b); for (db i = 0; i < a; i += step) s += step * pow(1 - pow(i / a, n), 1 / n); printf("%lf", s * 4 * b); return 0; }

 

 

方法五:公式法

如果你知道 C/C++/Python 自带 \Gamma 函数,可以直接套公式qwq (当然这不是本题出题的本意)

#include <bits/stdc++.h> signed main() { int n, a, b; scanf("%d%d%d", &n, &a, &b); double gamma1 = tgamma(1 + 1. / n); double gamma2 = tgamma(1 + 2. / n); printf("%lf", 4 * a * b * (gamma1 * gamma1) / gamma2); return 0; }