This documentation is automatically generated by online-judge-tools/verification-helper
View the Project on GitHub maspypy/library
#include "geo/minimum_enclosing_circle.hpp"
#include "geo/base.hpp" #include "random/shuffle.hpp" #include "geo/outcircle.hpp" // randomize を利用した expected O(N) アルゴリズム // 座標の 4 乗が登場!オーバーフロー注意! // return: {C,0,-1,-1} or {C,i,j,-1} or {C,i,j,k} // https://codeforces.com/problemset/problem/119/E // https://qoj.ac/contest/1452/problem/7934 template <typename REAL, typename T> tuple<Circle<REAL>, int, int, int> minimum_enclosing_circle(vc<Point<T>> points) { const int n = len(points); assert(n >= 1); if (n == 1) { Circle<REAL> C(points[0].x, points[0].y, 0); return {C, 0, -1, -1}; } vc<int> I(n); FOR(i, n) I[i] = i; shuffle(I); points = rearrange(points, I); tuple<int, int, int> c = {0, -1, -1}; auto contain = [&](Point<T> p) -> bool { auto [i, j, k] = c; if (j == -1) { return p == points[i]; } if (k == -1) { return (points[i] - p).dot(points[j] - p) <= 0; } return outcircle_side(points[i], points[j], points[k], p) >= 0; }; FOR(i, 1, n) { if (contain(points[i])) continue; c = {0, i, -1}; FOR(j, 1, i) { if (contain(points[j])) continue; c = {i, j, -1}; FOR(k, j) { if (contain(points[k])) continue; c = {i, j, k}; } } } auto [i, j, k] = c; if (k == -1) { REAL x1 = points[i].x; REAL y1 = points[i].y; REAL x2 = points[j].x; REAL y2 = points[j].y; Point<REAL> O = {0.5 * (x1 + x2), 0.5 * (y1 + y2)}; REAL r = sqrtl((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) / 2; Circle<REAL> C(O, r); return {C, I[i], I[j], -1}; } Circle<REAL> C = outcircle<REAL>(points[i], points[j], points[k]); return {C, I[i], I[j], I[k]}; }
#line 2 "geo/base.hpp" template <typename T> struct Point { T x, y; Point() : x(0), y(0) {} template <typename A, typename B> Point(A x, B y) : x(x), y(y) {} template <typename A, typename B> Point(pair<A, B> p) : x(p.fi), y(p.se) {} Point operator+=(const Point p) { x += p.x, y += p.y; return *this; } Point operator-=(const Point p) { x -= p.x, y -= p.y; return *this; } Point operator+(Point p) const { return {x + p.x, y + p.y}; } Point operator-(Point p) const { return {x - p.x, y - p.y}; } bool operator==(Point p) const { return x == p.x && y == p.y; } bool operator!=(Point p) const { return x != p.x || y != p.y; } Point operator-() const { return {-x, -y}; } Point operator*(T t) const { return {x * t, y * t}; } Point operator/(T t) const { return {x / t, y / t}; } bool operator<(Point p) const { if (x != p.x) return x < p.x; return y < p.y; } T dot(const Point& other) const { return x * other.x + y * other.y; } T det(const Point& other) const { return x * other.y - y * other.x; } double norm() { return sqrtl(x * x + y * y); } double angle() { return atan2(y, x); } Point rotate(double theta) { static_assert(!is_integral<T>::value); double c = cos(theta), s = sin(theta); return Point{c * x - s * y, s * x + c * y}; } Point rot90(bool ccw) { return (ccw ? Point{-y, x} : Point{y, -x}); } }; #ifdef FASTIO template <typename T> void rd(Point<T>& p) { fastio::rd(p.x), fastio::rd(p.y); } template <typename T> void wt(Point<T>& p) { fastio::wt(p.x); fastio::wt(' '); fastio::wt(p.y); } #endif // A -> B -> C と進むときに、左に曲がるならば +1、右に曲がるならば -1 template <typename T> int ccw(Point<T> A, Point<T> B, Point<T> C) { T x = (B - A).det(C - A); if (x > 0) return 1; if (x < 0) return -1; return 0; } template <typename REAL, typename T, typename U> REAL dist(Point<T> A, Point<U> B) { REAL dx = REAL(A.x) - REAL(B.x); REAL dy = REAL(A.y) - REAL(B.y); return sqrt(dx * dx + dy * dy); } // ax+by+c template <typename T> struct Line { T a, b, c; Line(T a, T b, T c) : a(a), b(b), c(c) {} Line(Point<T> A, Point<T> B) { a = A.y - B.y, b = B.x - A.x, c = A.x * B.y - A.y * B.x; } Line(T x1, T y1, T x2, T y2) : Line(Point<T>(x1, y1), Point<T>(x2, y2)) {} template <typename U> U eval(Point<U> P) { return a * P.x + b * P.y + c; } template <typename U> T eval(U x, U y) { return a * x + b * y + c; } // 同じ直線が同じ a,b,c で表現されるようにする void normalize() { static_assert(is_same_v<T, int> || is_same_v<T, long long>); T g = gcd(gcd(abs(a), abs(b)), abs(c)); a /= g, b /= g, c /= g; if (b < 0) { a = -a, b = -b, c = -c; } if (b == 0 && a < 0) { a = -a, b = -b, c = -c; } } bool is_parallel(Line other) { return a * other.b - b * other.a == 0; } bool is_orthogonal(Line other) { return a * other.a + b * other.b == 0; } }; template <typename T> struct Segment { Point<T> A, B; Segment(Point<T> A, Point<T> B) : A(A), B(B) {} Segment(T x1, T y1, T x2, T y2) : Segment(Point<T>(x1, y1), Point<T>(x2, y2)) {} bool contain(Point<T> C) { T det = (C - A).det(B - A); if (det != 0) return 0; return (C - A).dot(B - A) >= 0 && (C - B).dot(A - B) >= 0; } Line<T> to_Line() { return Line(A, B); } }; template <typename REAL> struct Circle { Point<REAL> O; REAL r; Circle(Point<REAL> O, REAL r) : O(O), r(r) {} Circle(REAL x, REAL y, REAL r) : O(x, y), r(r) {} template <typename T> bool contain(Point<T> p) { REAL dx = p.x - O.x, dy = p.y - O.y; return dx * dx + dy * dy <= r * r; } }; #line 2 "random/base.hpp" u64 RNG_64() { static u64 x_ = u64(chrono::duration_cast<chrono::nanoseconds>(chrono::high_resolution_clock::now().time_since_epoch()).count()) * 10150724397891781847ULL; x_ ^= x_ << 7; return x_ ^= x_ >> 9; } u64 RNG(u64 lim) { return RNG_64() % lim; } ll RNG(ll l, ll r) { return l + RNG_64() % (r - l); } #line 2 "random/shuffle.hpp" template <typename T> void shuffle(vc<T>& A) { FOR(i, len(A)) { int j = RNG(0, i + 1); if (i != j) swap(A[i], A[j]); } } #line 1 "geo/triangle_area.hpp" template <typename REAL, typename T> REAL triangle_area(Point<T> A, Point<T> B, Point<T> C) { return abs((B - A).det(C - A)) * 0.5; } #line 4 "geo/outcircle.hpp" template <typename REAL, typename T> Circle<REAL> outcircle(Point<T> A, Point<T> B, Point<T> C) { REAL b1 = B.x - A.x, b2 = B.y - A.y; REAL c1 = C.x - A.x, c2 = C.y - A.y; REAL bb = (b1 * b1 + b2 * b2) / 2; REAL cc = (c1 * c1 + c2 * c2) / 2; REAL det = b1 * c2 - b2 * c1; REAL x = (bb * c2 - b2 * cc) / det; REAL y = (b1 * cc - bb * c1) / det; REAL r = sqrt(x * x + y * y); x += A.x, y += A.y; return Circle<REAL>(x, y, r); } // ABC の外接円に対して内外どちらにあるか // 中:1, 境界:0, 外:-1 // 座標の 4 乗がオーバーフローしないようにする template <typename T> int outcircle_side(Point<T> A, Point<T> B, Point<T> C, Point<T> p) { T d = (B - A).det(C - A); assert(d != 0); if (d < 0) swap(B, C); array<Point<T>, 3> pts = {A, B, C}; array<array<T, 3>, 3> mat; FOR(i, 3) { T dx = pts[i].x - p.x, dy = pts[i].y - p.y; mat[i][0] = dx, mat[i][1] = dy, mat[i][2] = dx * dx + dy * dy; } T det = 0; det += mat[0][0] * (mat[1][1] * mat[2][2] - mat[1][2] * mat[2][1]); det += mat[0][1] * (mat[1][2] * mat[2][0] - mat[1][0] * mat[2][2]); det += mat[0][2] * (mat[1][0] * mat[2][1] - mat[1][1] * mat[2][0]); if (det == 0) return 0; return (det > 0 ? 1 : -1); } #line 4 "geo/minimum_enclosing_circle.hpp" // randomize を利用した expected O(N) アルゴリズム // 座標の 4 乗が登場!オーバーフロー注意! // return: {C,0,-1,-1} or {C,i,j,-1} or {C,i,j,k} // https://codeforces.com/problemset/problem/119/E // https://qoj.ac/contest/1452/problem/7934 template <typename REAL, typename T> tuple<Circle<REAL>, int, int, int> minimum_enclosing_circle(vc<Point<T>> points) { const int n = len(points); assert(n >= 1); if (n == 1) { Circle<REAL> C(points[0].x, points[0].y, 0); return {C, 0, -1, -1}; } vc<int> I(n); FOR(i, n) I[i] = i; shuffle(I); points = rearrange(points, I); tuple<int, int, int> c = {0, -1, -1}; auto contain = [&](Point<T> p) -> bool { auto [i, j, k] = c; if (j == -1) { return p == points[i]; } if (k == -1) { return (points[i] - p).dot(points[j] - p) <= 0; } return outcircle_side(points[i], points[j], points[k], p) >= 0; }; FOR(i, 1, n) { if (contain(points[i])) continue; c = {0, i, -1}; FOR(j, 1, i) { if (contain(points[j])) continue; c = {i, j, -1}; FOR(k, j) { if (contain(points[k])) continue; c = {i, j, k}; } } } auto [i, j, k] = c; if (k == -1) { REAL x1 = points[i].x; REAL y1 = points[i].y; REAL x2 = points[j].x; REAL y2 = points[j].y; Point<REAL> O = {0.5 * (x1 + x2), 0.5 * (y1 + y2)}; REAL r = sqrtl((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) / 2; Circle<REAL> C(O, r); return {C, I[i], I[j], -1}; } Circle<REAL> C = outcircle<REAL>(points[i], points[j], points[k]); return {C, I[i], I[j], I[k]}; }