This documentation is automatically generated by online-judge-tools/verification-helper
#define PROBLEM "https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/4/CGL_4_B"
#define ERROR 0.000001
#include "../../Src/GeometryZ2/Distance/FurthestPairOfPoints.hpp"
#include "../../Src/Utility/FloatingMarkerShift.hpp"
using namespace zawa;
using namespace geometryZ2;
#include <iostream>
#include <iomanip>
#include <cmath>
int main() {
std::cin.tie(nullptr);
std::cout.tie(nullptr);
std::ios::sync_with_stdio(false);
int N;
std::cin >> N;
PointCloud P(N);
for (auto& p : P) {
std::string x, y;
std::cin >> x >> y;
p = {FloatingMarkerShift(x, 6), FloatingMarkerShift(y, 6)};
}
auto [i, j] = FurthestPairOfPoints<int>(P);
std::cout << std::fixed << std::setprecision(8) << sqrtl(DistanceSquare(P[i], P[j])) / 1000000.0l << '\n';
}
#line 1 "Test/AOJ/CGL_4_B.test.cpp"
#define PROBLEM "https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/4/CGL_4_B"
#define ERROR 0.000001
#line 2 "Src/GeometryZ2/Distance/FurthestPairOfPoints.hpp"
#line 2 "Src/GeometryZ2/PointCloud.hpp"
#line 2 "Src/GeometryZ2/Point.hpp"
#line 2 "Src/Template/TypeAlias.hpp"
#include <cstdint>
#include <cstddef>
namespace zawa {
using i16 = std::int16_t;
using i32 = std::int32_t;
using i64 = std::int64_t;
using i128 = __int128_t;
using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;
using usize = std::size_t;
} // namespace zawa
#line 2 "Src/GeometryZ2/Zahlen.hpp"
#line 4 "Src/GeometryZ2/Zahlen.hpp"
#include <cassert>
namespace zawa {
namespace geometryZ2 {
using Zahlen = i64;
namespace internal {
constexpr i32 positive{1};
constexpr i32 zero{0};
constexpr i32 negative{-1};
} // namespace internal
constexpr i32 Sign(Zahlen value) {
if (value < 0) return internal::negative;
if (value > 0) return internal::positive;
return internal::zero;
}
constexpr bool Positive(Zahlen value) {
return Sign(value) == internal::positive;
}
constexpr bool Zero(Zahlen value) {
return Sign(value) == internal::zero;
}
constexpr bool Negative(Zahlen value) {
return Sign(value) == internal::negative;
}
constexpr Zahlen Abs(Zahlen value) {
return (value > 0 ? value : -value);
}
constexpr Zahlen Square(Zahlen value) {
return value * value;
}
} // namespace geometryZ2
} // namespace zawa
#line 5 "Src/GeometryZ2/Point.hpp"
#include <algorithm>
#include <iostream>
#line 9 "Src/GeometryZ2/Point.hpp"
#include <limits>
namespace zawa {
namespace geometryZ2 {
class Point {
private:
Zahlen x_{}, y_{};
static constexpr i32 origin{0};
static constexpr i32 firstQuadrant{1};
static constexpr i32 secondQuadrant{2};
static constexpr i32 thirdQuadrant{-2};
static constexpr i32 forthQuadrant{-1};
public:
/* constructor */
Point() = default;
Point(const Point& p) : x_{p.x()}, y_{p.y()} {}
Point(Zahlen x, Zahlen y) : x_{x}, y_{y} {}
/* getter setter */
Zahlen& x() {
return x_;
}
const Zahlen& x() const {
return x_;
}
Zahlen& y() {
return y_;
}
const Zahlen& y() const {
return y_;
}
/* operator */
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;
}
friend Point operator+(const Point& p0, const Point& p1) {
return Point{p0} += p1;
}
Point& operator-=(const Point& p) {
x() -= p.x();
y() -= p.y();
return *this;
}
friend Point operator-(const Point& p0, const Point& p1) {
return Point{p0} -= p1;
}
Point& operator*=(Zahlen k) {
x() *= k;
y() *= k;
return *this;
}
friend Point operator*(const Point& p, Zahlen k) {
return Point{p} *= k;
}
friend Point operator*(Zahlen k, const Point& p) {
return Point{p} *= k;
}
Point& operator/=(Zahlen k) {
assert(k);
assert(x() % k == 0);
assert(y() % k == 0);
x() /= k;
y() /= k;
return *this;
}
friend Point operator/(const Point& p, Zahlen k) {
return Point{p} /= k;
}
friend bool operator==(const Point& p0, const Point& p1) {
return p0.x() == p1.x() and p0.y() == p1.y();
}
friend bool operator!=(const Point& p0, const Point& p1) {
return p0.x() != p1.x() or p0.y() != p1.y();
}
friend bool operator<(const Point& p0, const Point& p1) {
if (p0.x() != p1.x()) return p0.x() < p1.x();
else return p0.y() < p1.y();
}
friend bool operator<=(const Point& p0, const Point& p1) {
return (p0 < p1) or (p0 == p1);
}
friend bool operator>(const Point& p0, const Point& p1) {
if (p0.x() != p1.x()) return p0.x() > p1.x();
else return p0.y() > p1.y();
}
friend bool operator>=(const Point& p0, const Point& p1) {
return (p0 > p1) or (p0 == p1);
}
friend std::istream& operator>>(std::istream& is, Point& p) {
is >> p.x() >> p.y();
return is;
}
friend std::ostream& operator<<(std::ostream& os, const Point& p) {
os << '(' << p.x() << ',' << p.y() << ')';
return os;
}
/* member function */
Zahlen normSquare() const {
return Square(x()) + Square(y());
}
bool isNormSquareOver(Zahlen d) const {
assert(!Negative(d));
auto [mn, mx]{std::minmax({ Abs(x()), Abs(y()) })};
if (mx and mx > d / mx) {
return true;
}
long long s1{Square(mn)}, s2{Square(mx)};
if (s1 > d - s2) {
return true;
}
return false;
}
bool isNormSquareOverflow() const {
return isNormSquareOver(std::numeric_limits<Zahlen>::max());
}
i32 area() const {
if (x_ == 0 and y_ == 0) return origin;
if (x_ <= 0 and y_ < 0) return thirdQuadrant;
if (x_ > 0 and y_ <= 0) return forthQuadrant;
if (x_ >= 0 and y_ > 0) return firstQuadrant;
return secondQuadrant;
}
/* static member */
static bool ArgComp(const Point& p0, const Point& p1) {
if (p0.area() != p1.area()) return p0.area() < p1.area();
Zahlen cross{Cross(p0, p1)};
return (!Zero(cross) ? Positive(cross) : p0.normSquare() < p1.normSquare());
}
/* friend function */
friend Zahlen Dot(const Point& p0, const Point& p1) {
return p0.x() * p1.x() + p0.y() * p1.y();
}
friend Zahlen Cross(const Point& p0, const Point& p1) {
return p0.x() * p1.y() - p0.y() * p1.x();
}
};
using Vector = Point;
} // namespace geometryZ2
} // namespace zawa
#line 4 "Src/GeometryZ2/PointCloud.hpp"
#line 6 "Src/GeometryZ2/PointCloud.hpp"
#include <vector>
namespace zawa {
namespace geometryZ2 {
using PointCloud = std::vector<Point>;
void ArgSort(PointCloud& p) {
std::sort(p.begin(), p.end(), Point::ArgComp);
}
} // namespace geometryZ2
} // namespace zawa
#line 2 "Src/GeometryZ2/ConvexHull.hpp"
#line 2 "Src/GeometryZ2/Polygon.hpp"
#line 2 "Src/GeometryZ2/Relation.hpp"
#line 5 "Src/GeometryZ2/Relation.hpp"
namespace zawa {
namespace geometryZ2 {
enum RELATION {
// p0 -> p1 -> p2の順で直線上に並んでいる
ONLINE_FRONT = -2,
// (p1 - p0) -> (p2 - p0)が時計回りになっている
CLOCKWISE = -1,
// p0 -> p2 -> p1の順で直線上に並んでいる
ON_SEGMENT = 0,
// (p1 - p0) -> (p2 - p0)が反時計回りになっている
COUNTER_CLOCKWISE = +1,
// p2 -> p0 -> p1、またはp1 -> p0 -> p2の順で直線上に並んでいる
ONLINE_BACK = +2
};
RELATION Relation(const Point& p0, const Point& p1, const Point& p2) {
Point a{p1 - p0}, b{p2 - p0};
if (Positive(Cross(a, b))) return COUNTER_CLOCKWISE;
if (Negative(Cross(a, b))) return CLOCKWISE;
if (Negative(Dot(a, b))) return ONLINE_BACK;
if (a.normSquare() < b.normSquare()) return ONLINE_FRONT;
return ON_SEGMENT;
};
} // namespace geometryZ2
} // namespace zawa
#line 6 "Src/GeometryZ2/Polygon.hpp"
#line 9 "Src/GeometryZ2/Polygon.hpp"
#include <iterator>
#include <type_traits>
#line 12 "Src/GeometryZ2/Polygon.hpp"
namespace zawa {
namespace geometryZ2 {
class Polygon {
private:
std::vector<Point> data_;
public:
usize size() const {
return data_.size();
}
/* constructor */
Polygon() = default;
Polygon(const Polygon& polygon) : data_{polygon.data_} {}
Polygon(const std::vector<Point>& data) : data_{data} {}
Polygon(usize n) : data_{n} {
assert(n >= static_cast<usize>(3));
}
/* operator */
Polygon& operator=(const Polygon& polygon) {
data_ = polygon.data_;
return *this;
}
Point& operator[](usize i) {
assert(i < size());
return data_[i];
}
const Point& operator[](usize i) const {
assert(i < size());
return data_[i];
}
friend std::istream& operator>>(std::istream& is, Polygon& polygon) {
for (size_t i{} ; i < polygon.size() ; i++) {
is >> polygon[i];
}
return is;
}
friend std::ostream& operator<<(std::ostream& os, const Polygon& polygon) {
for (usize i{} ; i < polygon.size() ; i++) {
std::cout << polygon[i] << (i + 1 == polygon.size() ? "" : " ");
}
return os;
}
/* member function */
void reserve(usize n) {
data_.reserve(n);
}
void pushBack(const Point& p) {
data_.push_back(p);
}
void emplaceBack(Zahlen x, Zahlen y) {
data_.emplace_back(x, y);
}
template <class RandomAccessIterator>
void insert(usize n, RandomAccessIterator first, RandomAccessIterator last) {
assert(n <= size());
data_.insert(std::next(data_.begin(), n), first, last);
}
void orderRotate(usize i) {
assert(i < size());
std::rotate(data_.begin(), data_.begin() + i, data_.end());
}
template <class F>
void normalForm(const F& func) {
auto index{std::distance(data_.begin(), std::min_element(data_.begin(), data_.end(), func))};
orderRotate(index);
}
void normalForm() {
auto index{std::distance(data_.begin(), std::min_element(data_.begin(), data_.end()))};
orderRotate(index);
}
template <class F>
Polygon normalFormed(const F& func = [](const Point& a, const Point& b) -> bool { return a < b; }) const {
Polygon res{*this};
res.normalForm(func);
return res;
}
Polygon normalFormed() {
Polygon res{*this};
res.normalForm();
return res;
}
bool isConvex() const {
assert(size() >= static_cast<usize>(3));
for (usize i{} ; i < size() ; i++) {
if (Relation(data_[i], data_[i+1==size()?0:i+1], data_[i+2>=size()?i+2-size():i+2])
== CLOCKWISE) {
return false;
}
}
return true;
}
Zahlen areaTwice() const {
assert(size() >= static_cast<usize>(3));
Zahlen res{};
for (usize i{1} ; i < size() ; i++) {
res += Cross(data_[i] - data_[0], data_[i+1==size()?0:i+1] - data_[0]);
}
return res;
}
Polygon subtriangle(usize i, usize j, usize k) const {
assert(i < size());
assert(j < size());
assert(k < size());
return Polygon{std::vector<Point>{ data_[i], data_[j], data_[k] }};
}
};
}
} // namespace zawa
#line 7 "Src/GeometryZ2/ConvexHull.hpp"
#line 13 "Src/GeometryZ2/ConvexHull.hpp"
namespace zawa {
namespace geometryZ2 {
template <bool strictly>
Polygon ConvexHull(const PointCloud& p) {
PointCloud cp{p};
std::sort(cp.begin(), cp.end());
cp.erase(std::unique(cp.begin(), cp.end()), cp.end());
if (cp.size() <= 2u) {
return Polygon{cp};
}
PointCloud lower;
lower.reserve(p.size());
for (auto it{cp.begin()} ; it != cp.end() ; it++) {
lower.push_back(*it);
while (lower.size() >= 3u) {
if (Relation(lower[lower.size() - 3], lower[lower.size() - 2], lower[lower.size() - 1]) == COUNTER_CLOCKWISE) {
break;
}
if constexpr (!strictly) {
if (Relation(lower[lower.size() - 3], lower[lower.size() - 2], lower[lower.size() - 1]) == ONLINE_FRONT) {
break;
}
}
std::swap(lower[lower.size() - 2], lower[lower.size() - 1]);
lower.pop_back();
}
}
PointCloud upper;
upper.reserve(p.size());
for (auto it{cp.rbegin()} ; it != cp.rend() ; it++) {
upper.push_back(*it);
while (upper.size() >= 3u) {
if (Relation(upper[upper.size() - 3], upper[upper.size() - 2], upper[upper.size() - 1]) == COUNTER_CLOCKWISE) {
break;
}
if constexpr (!strictly) {
if (Relation(upper[upper.size() - 3], upper[upper.size() - 2], upper[upper.size() - 1]) == ONLINE_FRONT) {
break;
}
}
std::swap(upper[upper.size() - 2], upper[upper.size() - 1]);
upper.pop_back();
}
}
Polygon res;
res.reserve(lower.size() + upper.size() - 2);
res.insert(res.size(), lower.begin(), lower.end());
res.insert(res.size(), std::next(upper.begin()), std::prev(upper.end()));
return res;
}
} // namespace geometryZ2
} // namespace zawa
#line 2 "Src/GeometryZ2/Distance/PointAndPoint.hpp"
#line 5 "Src/GeometryZ2/Distance/PointAndPoint.hpp"
namespace zawa {
namespace geometryZ2 {
Zahlen DistanceSquare(const Point& p0, const Point& p1) {
return Vector{p1 - p0}.normSquare();
}
} // namespace geometryZ2
} // namespace zawa
#line 6 "Src/GeometryZ2/Distance/FurthestPairOfPoints.hpp"
#line 8 "Src/GeometryZ2/Distance/FurthestPairOfPoints.hpp"
namespace zawa {
namespace geometryZ2 {
template <class T = usize>
std::pair<T, T> FurthestPairOfPoints(const PointCloud& P) {
assert(std::ssize(P) >= 2);
const auto ch = ConvexHull<true>(P);
if (std::ssize(ch) == 1) { // 全部の点が同じ所にある -> 何でも良い
return {T{0}, T{1}};
}
usize mini = 0, minj = 1;
Zahlen mind = DistanceSquare(ch[mini], ch[minj]);
if (std::ssize(ch) > 2) {
usize idx = 0, jdx = 0;
for (usize i = 1 ; i < ch.size() ; i++) {
if (ch[idx].x() > ch[i].x()) idx = i;
if (ch[jdx].x() < ch[i].x()) jdx = i;
}
const usize sidx = idx, sjdx = jdx;
while (idx != sjdx or jdx != sidx) {
if (mind < DistanceSquare(ch[idx], ch[jdx])) {
mind = DistanceSquare(ch[idx], ch[jdx]);
mini = idx;
minj = jdx;
}
if (Cross(ch[idx+1==ch.size()?0:idx+1] - ch[idx], ch[jdx+1==ch.size()?0:jdx+1] - ch[jdx]) < 0) {
idx = idx+1==ch.size()?0:idx+1;
}
else {
jdx = jdx+1==ch.size()?0:jdx+1;
}
}
}
const T n = static_cast<T>(P.size());
T i = n, j = n;
for (usize k = 0 ; k < P.size() ; k++) {
if (i == n and P[k] == ch[mini]) i = k;
if (j == n and P[k] == ch[minj]) j = k;
}
return {i, j};
}
} // namespace geometryZ2
} // namespace zawa
#line 2 "Src/Utility/FloatingMarkerShift.hpp"
#line 4 "Src/Utility/FloatingMarkerShift.hpp"
#include <string>
#line 8 "Src/Utility/FloatingMarkerShift.hpp"
namespace zawa {
i64 FloatingMarkerShift(const std::string& S, u32 shift) {
static i64 lim10{std::numeric_limits<i64>::max() / 10};
assert(not S.empty());
i64 res{};
u32 moved{};
bool start{};
bool minus{S[0] == '-'};
for (u32 i{(u32)minus} ; i < S.size() ; i++) {
if (S[i] == '.') {
start = true;
}
else {
if (start) moved++;
assert(res < lim10);
res = res * 10;
assert(res < std::numeric_limits<i64>::max() - (S[i] - '0'));
res += S[i] - '0';
}
}
assert(moved <= shift);
while (moved < shift) {
moved++;
assert(res < lim10);
res = res * 10;
}
if (minus) res *= -1;
return res;
}
}// namespace zawa
#line 6 "Test/AOJ/CGL_4_B.test.cpp"
using namespace zawa;
using namespace geometryZ2;
#line 10 "Test/AOJ/CGL_4_B.test.cpp"
#include <iomanip>
#include <cmath>
int main() {
std::cin.tie(nullptr);
std::cout.tie(nullptr);
std::ios::sync_with_stdio(false);
int N;
std::cin >> N;
PointCloud P(N);
for (auto& p : P) {
std::string x, y;
std::cin >> x >> y;
p = {FloatingMarkerShift(x, 6), FloatingMarkerShift(y, 6)};
}
auto [i, j] = FurthestPairOfPoints<int>(P);
std::cout << std::fixed << std::setprecision(8) << sqrtl(DistanceSquare(P[i], P[j])) / 1000000.0l << '\n';
}