1 #include <bits/stdc++.h> 2 3 #define debug(x) std::cerr << "[" << __LINE__ << "]: " << #x << " = " << x << "\n" 4 5 using i64 = long long; 6 7 #define LEFT 1 8 #define RIGHT -1 9 #define COIN 0 10 typedef double db; 11 constexpr db eps = 1e-8; 12 namespace Geo { 13 inline db min(db a, db b) { return a < b ? a : b; } 14 inline db max(db a, db b) { return a > b ? a : b; } 15 struct Point { 16 db x, y; 17 Point(db x = 0, db y = 0) : x(x), y(y) {} 18 Point operator+(const Point &b) { return Point(x + b.x, y + b.y); } 19 Point operator-(const Point &b) const { return Point(x - b.x, y - b.y); } 20 Point operator/(db m) { assert(m != 0); return Point(x / m, y / m); } 21 db operator*(const Point &b) { return (x * b.x) + (y * b.y); } 22 db operator^(const Point &b) { return (x * b.y) - (y * b.x); } 23 friend std::ostream& operator<<(std::ostream& os,const Point& p) { 24 os << "(" << p.x << "," << p.y << ")"; return os; 25 } 26 }; 27 typedef Point Vector; 28 struct Line { 29 Point a, b; 30 Line(db x1, db y1, db x2, db y2) :a(x1, y1), b(x2, y2) {} 31 }; 32 inline db cross(Point& o1, Point& a, Point& o2, Point& b) { return (a - o1) ^ (b - o2);} 33 Point operator*(const Point& a, db m) { return Point(a.x * m, a.y * m); } 34 Point operator*(db m, const Point& b) { return b * m; } 35 inline db sqr(db n) { return n * n;} 36 inline db dist (Point& a, Point& b) { return std::sqrt(sqr(a.x - b.x) + sqr(a.y - b.y));} 37 inline int dcmp(db x) { return std::abs(x) < eps ? COIN : (x < 0 ? RIGHT : LEFT);} 38 inline int side(Line& p, Point& q) { return dcmp(cross(p.a, q, p.a, p.b));} 39 bool onSegment(const Point& p1, const Point& p2, const Point& q) { return dcmp((p1 - q) ^ (p2 - q)) == 0 && dcmp((p1 - q) * (p2 - q)) <= 0;} 40 bool onSegment(const Line& l,const Point& q) { return onSegment(l.a, l.b, q); } 41 struct Polygon { 42 std::vector<Point> poly; 43 Polygon() {} 44 void add_point(Point& p) { poly.emplace_back(p); } 45 void add_point(db x, db y) { poly.emplace_back(x, y); } 46 friend std::ostream& operator<<(std::ostream& os, const Polygon& obj) { 47 for (int i = 0, sz = (int)obj.poly.size(); i < sz; i++) { 48 os << obj.poly[i] << " "; 49 } 50 return os; 51 } 52 bool inside(const Point& p) { 53 bool flag = false; 54 for (int i = 0, sz = (int)poly.size(), j = sz - 1; i < sz; j = i++) { 55 Point p1 = poly[i]; 56 Point p2 = poly[j]; 57 if (onSegment(p1, p2, p)) return true; 58 if ((dcmp(p1.y - p.y) > 0) != (dcmp(p2.y - p.y) > 0) \ 59 && dcmp(p.x - (p.y - p1.y) * (p1.x - p2.x) / (p1.y - p2.y) - p1.x) < 0 ) 60 { 61 flag = !flag; 62 } 63 } 64 return flag; 65 } 66 db Circumstance() { 67 db ans = 0; 68 for (int i = 0, sz = (int)poly.size(), j = sz - 1; i < sz; j = i++) { 69 ans += dist(poly[i], poly[j]); 70 } 71 return ans; 72 } 73 db Area(){ 74 db ans = 0; 75 Point assist(0, 0); 76 for (int i = 0, sz = (int)poly.size(), j = sz - 1; i < sz; j = i++) { 77 ans += cross(assist, poly[i], assist, poly[j]); 78 } 79 return std::abs(ans) * 0.5; 80 } 81 }; 82 83 bool intersect(Line& l1, Line& l2) { 84 if (max(l1.a.x, l1.b.x) < min(l2.a.x, l2.b.x)) return false;//排斥实验 85 if (max(l1.a.y, l1.b.y) < min(l2.a.y, l2.b.y)) return false; 86 if (max(l2.a.x, l2.b.x) < min(l1.a.x, l1.b.x)) return false; 87 if (max(l2.a.y, l2.b.y) < min(l1.a.y, l1.b.y)) return false; 88 if (cross(l1.a, l2.a, l1.a, l1.b) * cross(l1.a, l1.b, l1.a, l2.b) < 0) return false;//跨立实验 89 if (cross(l2.a, l1.a, l2.a, l2.b) * cross(l2.a, l2.b, l2.a, l1.b) < 0) return false; 90 return true; 91 } 92 Point interpoint(Point& a1, Point& a2, Point& b1, Point& b2) { 93 Vector v1 = Point(a2.x - a1.x, a2.y - a1.y); 94 Vector v2 = Point(b2.x - b1.x, b2.y - b1.y); 95 Vector u = a1 - b1; 96 db t = (v2 ^ u) / (v1 ^ v2); 97 return Point(a1.x + v1.x * t, a1.y + v1.y * t); 98 } 99 Point interpoint(Line& l1, Line& l2) { 100 return interpoint(l1.a, l1.b, l2.a, l2.b); 101 } 102 103 }; 104 using namespace Geo; 105 inline void swap(db& a, db& b) { db t = a; a = b; b = t;} 106 const int N = 2e5 + 5; 107 Point p[N]; 108 int main() { 109 110 int n; 111 scanf("%d", &n); 112 113 for (int i = 1; i <= n; i++) { 114 115 scanf("%lf %lf", &p[i].x, &p[i].y); 116 117 } 118 std::sort(p + 1, p + n + 1, [&](Point& a, Point& b){ 119 120 if (std::abs(a.y - b.y) < eps) return a.x < b.x; 121 return a.y < b.y; 122 }); 123 std::sort(p + 2, p + n + 1, [&](Point& pa, Point& pb) { 124 double x = cross(p[1], pa, p[1], pb); 125 if (x > eps) return true;//若大于0,就说明 p_pa 在 p_pb的左边 (根据右手螺旋定则) 126 if (std::abs(x) < eps && dist(p[0], pa) < dist(p[0], pb)) return true; 127 return false; 128 }); 129 130 Point* st = new Point [200005]; 131 132 int cnt = 1; 133 st[cnt] = p[1]; 134 135 for (int i = 2; i <= n; i++) { 136 while (cnt > 1 && cross(st[cnt - 1], st[cnt], st[cnt], p[i]) <= 0) { 137 --cnt; 138 } 139 ++cnt; 140 st[cnt] = p[i]; 141 } 142 st[cnt + 1] = p[1]; 143 double ans = 0; 144 for (int i = 1; i <= cnt; i++) { 145 ans += dist(st[i], st[i + 1]); 146 } 147 printf("%.2f\n", ans); 148 delete []st; 149 return 0; 150 }