Ans:12363.698850
注意到两个结论这题就做完了。
i)一个凸多边形面积最大时所有点共圆。
ii)一个凸多边形只要知道每条边的长度,最大面积与边的排列顺序无关。且
n≤50
时本质不同的边集数不多(
105
级别)。
第二点考虑一个取到最大面积的凸多边形可以交换任意相邻两条边使得最大面积不变,如果更大显然可以再交换回来使最大面积更大,矛盾。这样我们用类似冒泡排序的思想可以得到该边集的任意排列。
第一点并不会证啊QAQ哪位老司机教教我怎么证啊。。。
UPD:现在会证了(不知道是否是伪证)。我们已知等周定理:任意固定边长的图形,圆的面积最大。那么我们将共圆的凸多边形外面画上一个圆,并将每条边对应的弧附在边上。然后我们任意调整多边形。易知(1)该图形的周长不变(2)弧的面积和不变。根据定理可知这个图形面积最大时,该图形一定是圆。而由定义知一定可以取到。命题得证。
还是贴链接跑吧
然后我们枚举n,枚举划分,然后二分圆的半径。这里有个trick,就是所有点可以在圆的一侧。这个需要特判(我的方法是如果假设在圆两侧时二分失败则假设变为这个)。单调性仍然可以保证。
跑了
5min
左右。。。感觉asin还是太慢啊。。。(据说某JAVA老司机用acos代替asin快了10倍TAT)
#include <bits/stdc++.h>
using namespace std;
typedef long double ld;
int n, L[55], t = 0;
ld totans = 0, ans = 0, F[111];
int calc(int mx)
{
ld nw = F[n];
int s = 1;
for(int i = 2; i <= n; i++) if(L[i] == L[i - 1]) s++; else nw /= F[s], s = 1;
nw /= F[s];
ld l = mx / 2.0, r = 1e3;
while(l * (1.0 + 1e-15) < r) {
ld md = (l + r) / 2, tmp = 0;
for(int i = 1; i <= n; i++) tmp += asin((L[i] + 1) / 2.0 / md);
if(tmp < M_PI) r = md; else l = md;
}
ld tmp = 0;
for(int i = 1; i <= n; i++) tmp += asin((L[i] + 1) / 2.0 / r);
if(fabs(tmp - M_PI) > 1e-7) {
tmp = 0;
l = mx / 2.0, r = 1e3;
while(l * (1.0 + 1e-15) < r) {
ld md = (l + r) / 2, tmp = 0;
for(int i = 1; i <= n; i++) tmp += (L[i] + 1 != mx) ? asin((L[i] + 1) / 2.0 / md) : M_PI - asin(mx / 2.0 / md);
if(tmp > M_PI) r = md; else l = md;
}
tmp -= mx * sqrt(r * r - mx * mx / 4.0);
} else tmp = 0;
for(int i = 1; i <= n; i++) tmp += (L[i] + 1) * sqrt(r * r - (L[i] + 1) * (L[i] + 1) / 4.0) / 2.0;
ans += tmp * nw;
}
int Search(int x, int fir, int t = 0)
{
if(!x) return calc(L[t] + 1);
for(int i = fir; i <= x; i++) L[t + 1] = i, Search(x - i, i, t + 1), L[t + 1] = 0;
}
int main()
{
for(int i = 0; i <= 100; i++) F[i] = !i ? 1 : F[i - 1] * i;
for(n = 3; n <= 50; n++) ans = 0, Search(n - 3, 1), printf("ans %d is %.12lf\n", n, (double) (totans += ans / F[2 * n - 4] * F[n - 1] * F[n - 3]));
return 0;
}