[20220115专题选讲] Jewelry Size

题目大意

给定一圆内接多边形的边长 \(a_1,\cdots,a_n\),求该圆半径。

\(n\le 1000\),\(a_i\in [1,6000]\cap \mathbb{Z}\)。

source: ICPC Asia Regional Contest, Yokohama, 2021–03–17, Problem E

link: http://qoj.ac/contest/802/problem/2267

思路

二分 \(r\) 即可。

考虑在半径为 \(r\) 的圆上放置弦 \(a_i\),设 \(\theta_i\) 为 \(a_i\) 对应的圆心角(限制在 \([-\pi,\pi]\)),容易写出 \(\theta_i=2\operatorname{asin}(\dfrac {a_i}{2r})\).

第一种情况:最大弦不为优弦。

显然若答案为 \(r_0\),则 \(\sum\limits_i\theta_i=2\pi\).

容易知道 \(\dfrac{ {\mathrm{d}}\theta_i}{ {\mathrm{d}}r}=-\dfrac{2a_i}{r\sqrt{4r^2-a_i^2}}<0\),所以 \(\dfrac{ {\mathrm{d}}\sum\limits_i\theta_i}{ {\mathrm{d}}r}<0\),直接二分。

第二种情况:最大弦为优弦。

令 \(a_1=\max\{a_i\}\)

显然若答案为 \(r_0\),则 \(\theta_1-\sum\limits_{i\ne 1}\theta_i=0\).

单调性感性理解:

如果将 \(a_2,\cdots,a_n\) 相邻地放置在圆上,则总弧所对的弦一定随着 \(r\) 的增大而增大。

——神仙 zz

具体证明不会。

代码

一定要搞清是单调递减还是递增!

#include <cstdio>
#include <cmath>
#include <algorithm>

namespace mirai {

constexpr int MAXN = 1005;
constexpr long double PI = 3.14159265358979323846;
long double a[MAXN];

int main(int argc, char** argv) {
//  std::freopen("ala.out", "r", stdin);
  int n;
  std::scanf("%d", &n);
  for (int i = 0; i < n; ++i) {
    std::scanf("%Lf", &a[i]);
    if (a[i] > a[0]) {
      std::swap(a[i], a[0]);
    }
  }
  long double rl = a[0] / 2, rr = 1e9;
  auto f1 = [=](long double radius) -> long double {
    long double theta = 0;
    for (int i = 0; i < n; ++i) {
      theta += 2 * std::asin(a[i] / (radius * 2));
    }
    return theta;
  };
  while (rr - rl >= 5e-8) {
    long double mid = (rl + rr) / 2;
//    std::printf("%.10Lf %.10Lf %.10Lf\n", rl, rr, f1(mid));
    if (f1(mid) > 2 * PI) {
      rl = mid;
    } else {
      rr = mid;
    }
  }
  long double ans1 = (rl + rr) / 2;
  if (std::abs(f1(ans1) - 2 * PI) <= 5e-7) {
    std::printf("%.10Lf\n", ans1);
    return 0;
  }
  rl = a[0] / 2;
  rr = 1e9;
  auto f2 = [=](long double radius) -> long double {
    long double theta = 2 * std::asin(a[0] / (radius * 2));
    for (int i = 1; i < n; ++i) {
      theta -= 2 * std::asin(a[i] / (radius * 2));
    }
    return theta;
  };
//  std::puts("---------------------------");
  int cnt = 0;
  while (rr - rl >= 5e-8) {
    ++cnt;
    if (cnt == 100) {
      break;
    }
    long double mid = (rl + rr) / 2;
//    std::printf("%.10Lf %.10Lf %.10Lf\n", rl, rr, f2(mid));
    if (f2(mid) < 0) {
      rr = mid;
    } else {
      rl = mid;
    }
  }
  std::printf("%.10Lf\n", (rl + rr) / 2);
  return 0;
}

}

int main(int argc, char** argv) {
  return mirai::main(argc, argv);
}