Problem:
Convex Hull
Find the convex hull of a given set of points
P
. In other words, find the smallest convex polygon containing all the points of
P
. Here, in a convex polygon, all interior angles are less than or equal to 180 degrees.
Please note that you should find all the points of
P
on both corner and boundary of the convex polygon.
Input
n
x
1
y
1
x
2
y
2
:
x
n
y
n
The first integer
n
is the number of points in
P
. The coordinate of the
i
-th point
p
i
is given by two integers
x
i
and
y
i
.
Output
In the first line, print the number of points on the corner/boundary of the convex polygon. In the following lines, print
x y
coordinates of the set of points.
The coordinates should be given in the order of counter-clockwise visit of them starting from the point in
P
with the minimum
y
-coordinate, or the leftmost such point in case of a tie.
Constraints
3 ≤
n
≤ 100000
-10000 ≤
x
i
,
y
i
≤ 10000
No point in the
P
will occur more than once.
Sample Input 1
7
2 1
0 0
1 2
2 2
4 2
1 3
3 3
Sample Output 1
5
0 0
2 1
4 2
3 3
1 3
Sample Input 2
4
0 0
2 2
0 2
0 1
Sample Output 2
4
0 0
2 2
0 2
0 1

Submitted solutions:

code_snippet: #include <bits/stdc++.h>

#define debug(a) cout << #a << " = " << (a) << ' '

using namespace std;

#define db long double
// #define db long long

const db EPS = 1e-9;

#define cross(p1, p2, p3) (p2 - p1).det(p3 - p1)
#define crossop(p1, p2, p3) sign(cross(p1, p2, p3))

int sign(db a) { return a < -EPS ? -1 : a > EPS; }
int cmp(db a, db b) { return sign(a - b); }

struct P {
    db x, y;
    P() {}
    P(db _x, db _y) : x(_x), y(_y) {}

    P operator +(P p) { return {x + p.x, y + p.y}; }
    P operator -(P p) { return {x - p.x, y - p.y}; }
    P operator *(db d) { return {x * d, y * d}; }
    P operator /(db d) { return {x / d, y / d}; }

    bool operator < (P p) const {
        int c = cmp(y, p.y);
        if (c) return c == -1;
        return cmp(x, p.x) == -1;
    }

    bool operator == (P p) const {
        return ! cmp(x, p.x) && ! cmp(y, p.y);
    }

    db abs2() { return x * x + y * y; }
    db abs() { return sqrt(x * x + y * y); }
    db distTo(P p) { return (*this - p).abs(); }
    db alpha() { return atan2(y, x); }
    db dot(P p) { return x * p.x + y * p.y; }
    db det(P p) { return x * p.y - y * p.x; }

    int quadY() const { return sign(x) == 1 || (sign(x) == 0 && sign(y) >= 0); }
    int quadX() const { return sign(y) == 1 || (sign(y) == 0 && sign(x) >= 0); }

    P rot90() { return P(-y, x); }
    P rot(db alpha) { return { x * cos(alpha) - y * sin(alpha), x * sin(alpha) + y * cos(alpha)}; }
    P unit() { return *this / abs(); }
};

ostream & operator << (ostream &out, const P &A) {
    out << A.x << " " << A.y;
    return out;
}

// ------------------------------------
// 线段 / 线段

bool checkPll(P p1, P p2, P q1, P q2) {
    // 判断平行
    db a1 = cross(q1, q2, p1), a2 = - cross(q1, q2, p2);
    return sign(a1 + a2) == 0;
}

P isLL(P p1, P p2, P q1, P q2) {
    // 返回交点
    db a1 = cross(q1, q2, p1), a2 = - cross(q1, q2, p2);
    return (p1 * a2 + p2 * a1) / (a1 + a2);
}

bool intersect(db l1, db r1, db l2, db r2) {
    if (l1 > r1) swap(l1, r1);
    if (l2 > r2) swap(l2, r2);
    return cmp(r1, l2) >= 0 && cmp(r2, l1) >= 0;
}

bool isSS(P p1, P p2, P q1, P q2) {
    // 线段是否 不严格 相交
    // 1 : SS
    // 0 : unSS
    return intersect(p1.x, p2.x, q1.x, q2.x) &&
           intersect(p1.y, p2.y, q1.y, q2.y) && 
           crossop(p1, p2, q1) * crossop(p1, p2, q2) <= 0 && 
           crossop(q1, q2, p1) * crossop(q1, q2, p2) <= 0;
}

bool isSS_strict(P p1, P p2, P q1, P q2) {
    // 线段是否 严格 相交
    // 1 : SS
    // 0 : unSS
    return crossop(p1, p2, q1) * crossop(p1, p2, q2) < 0 && 
           crossop(q1, q2, p1) * crossop(q1, q2, p2) < 0;
}

bool isMiddle(db a, db m, db b) {
    return sign(a - m) == 0 || sign(b - m) == 0 || ((a < m) != (b < m));
}

bool isMiddle(P a, P m, P b) {
    return isMiddle(a.x, m.x, b.x) && isMiddle(a.y, m.y, b.y);
}

bool onSeg(P p1, P p2, P q) {
    // 是否在线段上 不严格
    return crossop(p1, p2, q) == 0 && isMiddle(p1, q, p2);
}

bool onSeg_strict(P p1, P p2, P q) {
    // 是否在线段上 严格
    return crossop(p1, p2, q) == 0 && 
           sign((q - p1).dot(p1 - p2)) * 
           sign((q - p2).dot(p1 - p2)) < 0;
}

P proj(P p1, P p2, P q) {
    // 求 q 到 p1 p2 的投影
    P dir = p2 - p1;
    return p1 + (p2 - p1) * (dir.dot(q - p1) / (dir).abs2());
}

P reflect(P p1, P p2, P q) {
    // 求 q 到 p1 p2 的反射点
    return proj(p1, p2, q) * 2 - q;
}

db nearest(P p1, P p2, P q) {
    // 求 q 到 p1 p2（线段）的最近点
    if (p1 == p2) return p1.distTo(q);
    P h = proj(p1, p2, q);
    if (isMiddle(p1, h, p2))
        return q.distTo(h);
    return min(p1.distTo(q), p2.distTo(q));
}

db disSS(P p1, P p2, P q1, P q2) {
    // 求线段 p1p2 和 线段 q1q2 的距离
    if (isSS(p1, p2, q1, q2)) return 0;
    db val1 = min(nearest(p1, p2, q1), nearest(p1, p2, q2));
    db val2 = min(nearest(q1, q2, p1), nearest(q1, q2, p2));
    return min(val1, val2);
}

db rad(P p1, P p2) {
    // 求向量 p1 p2 的角度（逆时针为正，顺时针为负，范围 (- pi, pi]）
    return atan2l(p1.det(p2), p1.dot(p2));
}

// ---------------------------------------------
// 多边形

db area(vector < P > ps) {
    // 求多边形面积（逆时针顺序给出点 -> 面积为正）
    db ret = 0; if (ps.size() == 0) return 0;
    for (int i = 0; i < (int)ps.size(); ++ i) {
        ret += ps[i].det(ps[(i + 1) % ps.size()]);
    }
    return ret / 2;
}

int contain(vector < P > ps, P p) {
    // 0 : outside
    // 1 : on_seg
    // 2 : inside

    // ps 是多边形，判断 p 在多边形 内部 / 线段上 / 外部
    int n = ps.size(), ret = 0;
    for (int i = 0; i < n; ++ i) {
        P u = ps[i], v = ps[(i + 1) % n];
        if (onSeg(u, v, p)) return 1;
        if (cmp(u.y, v.y) <= 0) swap(u, v);
        if (cmp(p.y, u.y) > 0 || cmp(p.y, v.y) <= 0)
            continue;
        ret ^= crossop(p, u, v) > 0;
    }
    return ret * 2;
}

vector < P > convexHull(vector < P > ps) {
    // 求凸包
    int n = ps.size();
    if (n <= 1) return ps;
    sort(ps.begin(), ps.end());
    vector < P > qs(n * 2);
    int k = 0;
    for (int i = 0; i < n; qs[k ++] = ps[i ++])
        while (k > 1 && crossop(qs[k - 2], qs[k - 1], ps[i]) <= 0)
            -- k;
    for (int i = n - 2, t = k; i >= 0; qs[k ++] = ps[i --])
        while (k > t && crossop(qs[k - 2], qs[k - 1], ps[i]) <= 0)
            -- k;
    qs.resize(k - 1);
    return qs;
}


vector < P > convexHullNonStrict(vector < P > ps) {
    // 求凸包（边界上的点也在凸包内）
    sort(ps.begin(), ps.end());
    ps.erase(unique(ps.begin(), ps.end()), ps.end());
    int n = ps.size();
    if (n <= 1) return ps;
    vector < P > qs(n * 2);
    int k = 0;
    for (int i = 0; i < n; qs[k ++] = ps[i ++])
        while (k > 1 && crossop(qs[k - 2], qs[k - 1], ps[i]) < 0)
            -- k;
    for (int i = n - 2, t = k; i >= 0; qs[k ++] = ps[i --])
        while (k > t && crossop(qs[k - 2], qs[k - 1], ps[i]) < 0)
            -- k;
    qs.resize(k - 1);
    return qs;
}

bool checkConvexHull(vector < P > p) {
    // 判断 p 多边形是否是凸包
    // 注意：不可通过再跑一次凸包判断点数是否相同
    // 因为点数相同 不能代表是凸包的连接方式
    int n = p.size();
    for (int i = 0; i < n; ++ i) {
        int pre = i - 1;
        int suf = i + 1;
        if (pre == -1) pre = n - 1;
        if (suf == n) suf = 0;
        if (crossop(p[i], p[suf], p[pre]) < 0)
            return 0;
    }
    return 1;
}

bool polar_cmpX(P a, P b) {
    // 极角排序 X 轴正半轴（包含）开始逆时针转
    if (a.quadX() != b.quadX()) 
        return a.quadX() > b.quadX();
    return sign(a.det(b)) > 0;
}

bool polar_cmpY(P a, P b) {
    // 极角排序 Y 轴负半轴（不包含）开始逆时针转
    if (a.quadY() != b.quadY()) 
        return a.quadY() > b.quadY();
    return sign(a.det(b)) > 0;
}

vector < P > MincowskySum(vector < P > p, vector < P > q) {
    // p, q 为两个凸包（顺序同方向），求这两个凸包的 闵可夫斯基和
    // 得保证 p, q 的第一个点 是排序后的
    // 即 尽可能在 convexHull 后再传入 p, q 
    if (p.size() == 0) return q;
    if (q.size() == 0) return p;
    vector < P > qs; qs.push_back(p[0] + q[0]);
    P p0 = p[0], q0 = q[0];
    for (int i = 0; i + 1 < (int)p.size(); ++ i)
        p[i] = p[i + 1] - p[i];
    for (int i = 0; i + 1 < (int)q.size(); ++ i)
        q[i] = q[i + 1] - q[i];
    p[p.size() - 1] = p0 - p[p.size() - 1];
    q[q.size() - 1] = q0 - q[q.size() - 1];
    qs.resize(p.size() + q.size() + 1);
    merge(p.begin(), p.end(), q.begin(), q.end(), qs.begin() + 1, polar_cmpY);
    for (int i = 1; i < (int)qs.size(); ++ i)
        qs[i] = qs[i] + qs[i - 1];
    qs.pop_back();
    return qs;
}

void solve() {
    int n; cin >> n;
    vector < P > p(n);
    for (int i = 0; i < n; ++ i) {
        cin >> p[i].x >> p[i].y;
    }

    vector < P > q = convexHullNonStrict(p);
    cout << q.size() << '\n';
    for (auto x : q) cout << x << '\n';
}


int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    int t = 1;
    while (t --) {
        solve();
    }
    return 0;
}
accuracy: 1
time_ms: 70
memory_kb: 11624
final_rank: 14

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int, int>
#define pll pair<ll, ll>
#define vi vector<int>
#define vl vector<ll>
#define ov4(a, b, c, d, name, ...) name
#define rep3(i, a, b, c) for(ll i = (a); i < (b); i += (c))
#define rep2(i, a, b) rep3(i, a, b, 1)
#define rep1(i, n) rep2(i, 0, n)
#define rep0(n) rep1(aaaaa, n)
#define rep(...) ov4(__VA_ARGS__, rep3, rep2, rep1, rep0)(__VA_ARGS__)
#define per(i, a, b) for(ll i = (a)-1; i >= (b); i--)
#define fore(e, v) for(auto&& e : v)
#define all(a) begin(a), end(a)
#define si(a) (int)(size(a))
#define lb(v, x) (lower_bound(all(v), x) - begin(v))
#define eb emplace_back

template<typename T, typename S> bool chmin(T& a, const S& b) { return a > b ? a = b, 1 : 0; }
template<typename T, typename S> bool chmax(T& a, const S& b) { return a < b ? a = b, 1 : 0; }

const int INF = 1e9 + 100;
const ll INFL = 3e18 + 100;

#define i128 __int128_t

struct _ {
   _() { cin.tie(0)->sync_with_stdio(0), cout.tie(0); }
} __;

template<class T> int sgn(T x) { return (x > 0) - (x < 0); }
template<class T>
struct Point {
    typedef Point P;
    T x, y;
    explicit Point(T x=0, T y=0) : x(x), y(y) {}
    bool operator<(P p) const { return tie(x, y) < tie(p.x, p.y); }
    bool operator==(P p) const { return tie(x, y) == tie(p.x, p.y); }
    P operator-(P p) const { return P(x - p.x, y - p.y); }
    P operator*(T d) const { return P(x * d, y * d); }
    P operator/(T d) const { return P(x / d, y / d); }
    T dot(P p) const { return x * p.x + y * p.y; }
    T cross(P p) const { return x * p.y - y * p.x; }
    T cross(P a, P b) const { return (a - *this).cross(b - *this); }
    T dist2() const { return x*x + y*y; }
    double dist() const { return sqrt((double)dist2()); }
    P perp() const { return P(-y, x); }
};

template<class P>
P lineProj(P a, P b, P p, bool refl=false) {
    P v = b - a;
    return p - v.perp() * (1 + refl) * v.cross(p - a) / v.dist2();
}

template<class P>
bool onSegment(P s, P e, P p) {
    return p.cross(s, e) == 0 and (s - p).dot(e - p) <= 0;
}

template<class P>
vector<P> segInter(P a, P b, P c, P d) {
    auto oa = c.cross(d, a), ob = c.cross(d, b),
         oc = a.cross(b, c), od = a.cross(b, d);
    if (sgn(oa) * sgn(ob) < 0 and sgn(oc) * sgn(od) < 0) {
        return {(a * ob - b * oa) / (ob - oa)};
    }
    set<P> s;
    if (onSegment(c, d, a)) s.insert(a);
    if (onSegment(c, d, b)) s.insert(b);
    if (onSegment(a, b, c)) s.insert(c);
    if (onSegment(a, b, d)) s.insert(d);
    return {all(s)};
}

template<class P>
double segDist(P s, P e, P p) {
    if (s == e) return (p - s).dist();
    auto d = (s - e).dist2(), t = min(d, max(.0, (p - s).dot(e - s)));
    return ((p - s) * d - (e - s) * t).dist() / d;
}

template<class P>
double segDist(P s1, P e1, P s2, P e2) {
    if (!segInter(s1, e1, s2, e2).empty()) return 0;
    auto a = min(segDist(s1, e1, s2), segDist(s1, e1, e2));
    auto b = min(segDist(s2, e2, s1), segDist(s2, e2, e1));
    return min(a, b);
}

template<class P>
pair<int, P> lineInter(P s1, P e1, P s2, P e2) {
    auto d = (e1 - s2).cross(e2 - s2);
    if(d == 0) {
        return {-(s1.cross(e1, s2) == 0), P(0, 0)};
    }
    auto p = s2.cross(e1, e2), q = s2.cross(e2, s1);
    return {1, (s1 * p + e1 * q) / d};
}

template<class T>
T polygonArea2(vector<Point<T>> &v) {
    T a = v.back().cross(v[0]);
    rep(i, si(v) - 1) a += v[i].cross(v[i + 1]);
    return a;
}



// speed star library
typedef Point<ll> P;
vector<P> convex_hull(vector<P> pts) {
    int n = si(pts), k = 0;
    if (n <= 2) return pts;
    sort(all(pts));
    vector<P> ch(2 * n);
    for(int i = 0; i < n; ch[k++] = pts[i++]) {
        while(k >= 2 and (ch[k - 1] - ch[k - 2]).cross(pts[i] - ch[k - 1]) < 0) --k;
    }
    for(int i = n - 2, t = k + 1; i >= 0; ch[k++] = pts[i--]) {
        while(k >= t and (ch[k - 1] - ch[k - 2]).cross(pts[i] - ch[k - 1]) < 0) --k;
    }
    ch.resize(k - 1);
    return ch;
}
#undef P

template<class P>
bool inPolygon(vector<P> &p, P a, bool strict = true) {
    int cnt = 0, n = si(p);
    rep(i, n) {
        P q = p[(i + 1) % n];
        if (onSegment(p[i], q, a)) return !strict;
        cnt ^= ((a.y < p[i].y) - (a.y < q.y)) * a.cross(p[i], q) > 0;
    }
    return cnt;
}

using P = Point<ll>;

int main() {
    int n;
    cin >> n;
    vector<P> v;
    rep(i, n) {
        ll x, y;
        cin >> x >> y;
        v.push_back(P(x, y));
    }

    auto cv = convex_hull(v);
    auto mp = *min_element(all(cv), [&](P p, P q) { return tie(p.y, p.x) < tie(q.y, q.x); });
    int midx = 0;
    rep(i, si(cv)) if (cv[i] == mp) midx = i;

    cout << si(cv) << endl;
    rep(i, si(cv)) {
        auto p = cv[(i + midx) % si(cv)];
        cout << p.x << " " << p.y << endl;
    }
}
accuracy: 1
time_ms: 30
memory_kb: 9704
final_rank: 7

code_snippet: // --- Start of include: ./../../akakoi_lib/template/template.cpp ---
// #pragma GCC target("avx2")
// #pragma GCC optimize("O3,unroll-loops")
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i,n) for(ll i=0;i<(ll)(n);++i)
bool chmin(auto& a, auto b) {return a>b?a=b,1:0;}
bool chmax(auto& a, auto b) {return a<b?a=b,1:0;}
// int main() {
//     ios::sync_with_stdio(false);
//     cin.tie(0);
// }
// --- End of include: ./../../akakoi_lib/template/template.cpp ---
// --- Start of include: ./../../akakoi_lib/geometry/geometry.cpp ---
/// @brief 幾何ライブラリ
namespace Geometry {
 using Real = long double;
 const Real EPS = 1e-9;

 bool almostEqual(Real a, Real b) { return abs(a - b) < EPS; }
 bool lessThan(Real a, Real b) { return a < b && !almostEqual(a, b); }
 bool greaterThan(Real a, Real b) { return a > b && !almostEqual(a, b); }
 bool lessThanOrEqual(Real a, Real b) { return a < b || almostEqual(a, b); }
 bool greaterThanOrEqual(Real a, Real b) { return a > b || almostEqual(a, b); }

 /// @brief ２次元平面上の位置ベクトル
 struct Point {
  Real x, y;
  Point() = default;
  Point(Real x, Real y) : x(x), y(y) {}

  Point operator+(const Point& p) const { return Point(x + p.x, y + p.y); }
  Point operator-(const Point& p) const { return Point(x - p.x, y - p.y); }
  Point operator*(Real k) const { return Point(x * k, y * k); }
  Point operator/(Real k) const { return Point(x / k, y / k); }

  /// @brief p との内積を返す
  Real dot(const Point& p) const { return x * p.x + y * p.y; }

  /// @brief p との外積を返す
  Real cross(const Point& p) const { return x * p.y - y * p.x; }

  /// @brief p1 と p2 を端点とするベクトルとの外積を返す
  Real cross(const Point& p1, const Point& p2) const { return (p1.x - x) * (p2.y - y) - (p1.y - y) * (p2.x - x); }

  /// @brief ２乗ノルムを返す
  Real norm() const { return x * x + y * y; }

  /// @brief ユークリッドノルムを返す
  Real abs() const { return sqrt(norm()); }

  /// @brief 偏角を返す
  Real arg() const { return atan2(y, x); }

  bool operator==(const Point& p) const { return almostEqual(x, p.x) && almostEqual(y, p.y); }
  friend istream& operator>>(istream& is, Point& p) { return is >> p.x >> p.y; }
 };

 /// @brief 直線
 struct Line {
  Point a, b;
  Line() = default;
  Line(const Point& _a, const Point& _b) : a(_a), b(_b) {}

  /// @brief 直線 Ax+By=C を定義する
  Line(const Real& A, const Real& B, const Real& C) {
   if (almostEqual(A, 0)) {
    assert(!almostEqual(B, 0));
    a = Point(0, C / B);
    b = Point(1, C / B);
   } else if (almostEqual(B, 0)) {
    a = Point(C / A, 0);
    b = Point(C / A, 1);
   } else if (almostEqual(C, 0)) {
    a = Point(0, C / B);
    b = Point(1, (C - A) / B);
   } else {
    a = Point(0, C / B);
    b = Point(C / A, 0);
   }
  }

  bool operator==(const Line& l) const { return a == l.a && b == l.b; }
  friend istream& operator>>(istream& is, Line& l) { return is >> l.a >> l.b; }
 };

 /// @brief 線分
 struct Segment : Line {
  Segment() = default;
  using Line::Line;
 };

 /// @brief 円
 struct Circle {
  Point center;  ///< 中心
  Real r;  ///< 半径

  Circle() = default;
  Circle(Real x, Real y, Real r) : center(x, y), r(r) {}
  Circle(Point _center, Real r) : center(_center), r(r) {}

  bool operator==(const Circle& C) const { return center == C.center && r == C.r; }
  friend istream& operator>>(istream& is, Circle& C) { return is >> C.center >> C.r; }
 };

 //-----------------------------------------------------------

 /// @brief 3点の進行方向
 enum Orientation {
  COUNTER_CLOCKWISE,  ///< 反時計回り
  CLOCKWISE,  ///< 時計回り
  ONLINE_BACK,
  ONLINE_FRONT,
  ON_SEGMENT
 };

 /// @brief 3点 p0, p1, p2 の進行方向を返す
 Orientation ccw(const Point& p0, const Point& p1, const Point& p2) {
  Point a = p1 - p0;
  Point b = p2 - p0;
  Real cross_product = a.cross(b);
  if (greaterThan(cross_product, 0)) return COUNTER_CLOCKWISE;
  if (lessThan(cross_product, 0)) return CLOCKWISE;
  if (lessThan(a.dot(b), 0)) return ONLINE_BACK;
  if (lessThan(a.norm(), b.norm())) return ONLINE_FRONT;
  return ON_SEGMENT;
 }

 string orientationToString(Orientation o) {
  switch (o) {
   case COUNTER_CLOCKWISE:
    return "COUNTER_CLOCKWISE";
   case CLOCKWISE:
    return "CLOCKWISE";
   case ONLINE_BACK:
    return "ONLINE_BACK";
   case ONLINE_FRONT:
    return "ONLINE_FRONT";
   case ON_SEGMENT:
    return "ON_SEGMENT";
   default:
    return "UNKNOWN";
  }
 }

 /// @brief ベクトル p の直線 p1, p2 への正射影ベクトルを返す
 Point projection(const Point& p1, const Point& p2, const Point& p) {
  Point base = p2 - p1;
  Real r = (p - p1).dot(base) / base.norm();
  return p1 + base * r;
 }

 /// @brief ベクトル p の直線 l への正射影ベクトルを返す
 Point projection(const Line& l, const Point& p) {
  Point base = l.b - l.a;
  Real r = (p - l.a).dot(base) / base.norm();
  return l.a + base * r;
 }

 /// @brief ベクトル p の直線 p1, p2 に対する鏡像ベクトルを返す
 Point reflection(const Point& p1, const Point& p2, const Point& p) {
  Point proj = projection(p1, p2, p);
  return proj * 2 - p;
 }

 /// @brief ベクトル p の直線 l に対する鏡像ベクトルを返す
 Point reflection(const Line& l, const Point& p) {
  Point proj = projection(l, p);
  return proj * 2 - p;
 }

 //-----------------------------------------------------------

 /// @brief 直線の平行判定
 bool isParallel(const Line& l1, const Line& l2) { return almostEqual((l1.b - l1.a).cross(l2.b - l2.a), 0); }
 /// @brief 直線の直交判定
 bool isOrthogonal(const Line& l1, const Line& l2) { return almostEqual((l1.b - l1.a).dot(l2.b - l2.a), 0); }
 /// @brief 線分の平行判定
 bool isParallel(const Segment& l1, const Segment& l2) { return almostEqual((l1.b - l1.a).cross(l2.b - l2.a), 0); }
 /// @brief 線分の直交判定
 bool isOrthogonal(const Segment& l1, const Segment& l2) { return almostEqual((l1.b - l1.a).dot(l2.b - l2.a), 0); }
 /// @brief 直線と線分の平行判定
 bool isParallel(const Line& l1, const Segment& l2) { return almostEqual((l1.b - l1.a).cross(l2.b - l2.a), 0); }
 /// @brief 直線と線分の直交判定
 bool isOrthogonal(const Line& l1, const Segment& l2) { return almostEqual((l1.b - l1.a).dot(l2.b - l2.a), 0); }
 /// @brief 線分と直線の平行判定
 bool isParallel(const Segment& l1, const Line& l2) { return almostEqual((l1.b - l1.a).cross(l2.b - l2.a), 0); }
 /// @brief 線分と直線の直交判定
 bool isOrthogonal(const Segment& l1, const Line& l2) { return almostEqual((l1.b - l1.a).dot(l2.b - l2.a), 0); }
 /// @brief 点が直線上にあるか判定
 bool isPointOnLine(const Point& p, const Line& l) { return almostEqual((l.b - l.a).cross(p - l.a), 0.0); }

 /// @brief 点が線分上にあるか判定
 bool isPointOnSegment(const Point& p, const Segment& s) {
  return lessThanOrEqual(min(s.a.x, s.b.x), p.x) &&
     lessThanOrEqual(p.x, max(s.a.x, s.b.x)) &&
     lessThanOrEqual(min(s.a.y, s.b.y), p.y) &&
     lessThanOrEqual(p.y, max(s.a.y, s.b.y)) &&
     almostEqual((s.b - s.a).cross(p - s.a), 0.0);
 }

 /// @brief 直線の交差判定
 bool isIntersecting(const Segment& s1, const Segment& s2) {
  Point p0p1 = s1.b - s1.a, p0p2 = s2.a - s1.a, p0p3 = s2.b - s1.a, p2p3 = s2.b - s2.a, p2p0 = s1.a - s2.a, p2p1 = s1.b - s2.a;
  Real d1 = p0p1.cross(p0p2), d2 = p0p1.cross(p0p3), d3 = p2p3.cross(p2p0), d4 = p2p3.cross(p2p1);
  if (lessThan(d1 * d2, 0) && lessThan(d3 * d4, 0)) return true;
  if (almostEqual(d1, 0.0) && isPointOnSegment(s2.a, s1)) return true;
  if (almostEqual(d2, 0.0) && isPointOnSegment(s2.b, s1)) return true;
  if (almostEqual(d3, 0.0) && isPointOnSegment(s1.a, s2)) return true;
  if (almostEqual(d4, 0.0) && isPointOnSegment(s1.b, s2)) return true;
  return false;
 }

 /// @brief 線分の交点を返す
 Point getIntersection(const Segment& s1, const Segment& s2) {
  assert(isIntersecting(s1, s2));
  auto cross = [](Point p, Point q) { return p.x * q.y - p.y * q.x; };
  Point base = s2.b - s2.a;
  Real d1 = abs(cross(base, s1.a - s2.a));
  Real d2 = abs(cross(base, s1.b - s2.a));
  Real t = d1 / (d1 + d2);
  return s1.a + (s1.b - s1.a) * t;
 }

 /// @brief 点と線分の距離を返す
 Real distancePointToSegment(const Point& p, const Segment& s) {
  Point proj = projection(s.a, s.b, p);
  if (isPointOnSegment(proj, s))
   return (p - proj).abs();
  else
   return min((p - s.a).abs(), (p - s.b).abs());
 }

 /// @brief 線分と線分の距離を返す
 Real distanceSegmentToSegment(const Segment& s1, const Segment& s2) {
  if (isIntersecting(s1, s2)) return 0.0;
  return min({distancePointToSegment(s1.a, s2),
     distancePointToSegment(s1.b, s2),
     distancePointToSegment(s2.a, s1),
     distancePointToSegment(s2.b, s1)});
 }

 //-----------------------------------------------------------

 /// @brief 多角形の面積を返す
 Real getPolygonArea(const vector<Point>& points) {
  int n = points.size();
  Real area = 0.0;
  for (int i = 0; i < n; i++) {
   int j = (i + 1) % n;
   area += points[i].x * points[j].y;
   area -= points[i].y * points[j].x;
  }
  return abs(area) / 2.0;
 }

 /// @brief 多角形が凸か判定
 bool isConvex(const vector<Point>& points) {
  int n = points.size();
  bool has_positive = false, has_negative = false;
  for (int i = 0; i < n; i++) {
   int j = (i + 1) % n;
   int k = (i + 2) % n;
   Point a = points[j] - points[i];
   Point b = points[k] - points[j];
   Real cross_product = a.cross(b);
   if (greaterThan(cross_product, 0)) has_positive = true;
   if (lessThan(cross_product, 0)) has_negative = true;
  }
  return !(has_positive && has_negative);
 }

 /// @brief 点が凸多角形の辺上に存在するか判定
 bool isPointOnPolygon(const vector<Point>& polygon, const Point& p) {
  int n = polygon.size();
  for (int i = 0; i < n; i++) {
   Point a = polygon[i];
   Point b = polygon[(i + 1) % n];
   Segment s(a, b);
   if (isPointOnSegment(p, s)) return true;
  }
  return false;
 }

 /// @brief 点が多角形の内部に存在するか判定（辺上は含まない）
 bool isPointInsidePolygon(const vector<Point>& polygon, const Point& p) {
  int n = polygon.size();
  bool inPolygon = false;
  for (int i = 0; i < n; i++) {
   Point a = polygon[i];
   Point b = polygon[(i + 1) % n];
   if (greaterThan(a.y, b.y)) swap(a, b);
   if (lessThanOrEqual(a.y, p.y) && lessThan(p.y, b.y) && greaterThan((b - a).cross(p - a), 0)) inPolygon = !inPolygon;
  }
  return inPolygon;
 }

 //-----------------------------------------------------------

 /// @brief 凸包を求める
 vector<Point> convexHull(vector<Point>& points, bool include_collinear = false) {
  int n = points.size();
  if (n <= 1) return points;
  sort(points.begin(), points.end(), [](const Point& l, const Point& r) -> bool {
   if (almostEqual(l.y, r.y)) return lessThan(l.x, r.x);
   return lessThan(l.y, r.y);
  });
  if (n == 2) return points;
  vector<Point> upper = {points[0], points[1]}, lower = {points[0], points[1]};
  for (int i = 2; i < n; i++) {
   while (upper.size() >= 2 && ccw(upper.end()[-2], upper.end()[-1], points[i]) != CLOCKWISE) {
    if (ccw(upper.end()[-2], upper.end()[-1], points[i]) == ONLINE_FRONT && include_collinear) break;
    upper.pop_back();
   }
   upper.push_back(points[i]);
   while (lower.size() >= 2 && ccw(lower.end()[-2], lower.end()[-1], points[i]) != COUNTER_CLOCKWISE) {
    if (ccw(lower.end()[-2], lower.end()[-1], points[i]) == ONLINE_FRONT && include_collinear) break;
    lower.pop_back();
   }
   lower.push_back(points[i]);
  }
  reverse(upper.begin(), upper.end());
  upper.pop_back();
  lower.pop_back();
  lower.insert(lower.end(), upper.begin(), upper.end());
  return lower;
 }

 /// @brief 凸包の直径を求める
 Real convexHullDiameter(const vector<Point>& hull) {
  int n = hull.size();
  if (n == 1) return 0;
  int k = 1;
  Real max_dist = 0;
  for (int i = 0; i < n; i++) {
   while (true) {
    int j = (k + 1) % n;
    Point dist1 = hull[i] - hull[j], dist2 = hull[i] - hull[k];
    max_dist = max(max_dist, dist1.abs());
    max_dist = max(max_dist, dist2.abs());
    if (dist1.abs() > dist2.abs())
     k = j;
    else
     break;
   }
   Point dist = hull[i] - hull[k];
   max_dist = max(max_dist, dist.abs());
  }
  return max_dist;
 }

 /// @brief 凸包を直線で切断して左側を返す
 vector<Point> cutPolygon(const vector<Point>& g, const Line& l) {
  auto isLeft = [](const Point& p1, const Point& p2, const Point& p) -> bool { return (p2 - p1).cross(p - p1) > 0; };
  vector<Point> newPolygon;
  int n = g.size();
  for (int i = 0; i < n; i++) {
   const Point& cur = g[i];
   const Point& next = g[(i + 1) % n];
   if (isLeft(l.a, l.b, cur)) newPolygon.push_back(cur);
   if ((isLeft(l.a, l.b, cur) && !isLeft(l.a, l.b, next)) || (!isLeft(l.a, l.b, cur) && isLeft(l.a, l.b, next))) {
    Real A1 = (next - cur).cross(l.a - cur);
    Real A2 = (next - cur).cross(l.b - cur);
    Point intersection = l.a + (l.b - l.a) * (A1 / (A1 - A2));
    newPolygon.push_back(intersection);
   }
  }
  return newPolygon;
 }

 //-----------------------------------------------------------

 /// @brief 最近点対の距離を求める
 /// @note points は x 座標でソートされている必要がある
 Real closestPair(vector<Point>& points, int l, int r) {
  if (r - l <= 1) return numeric_limits<Real>::max();
  int mid = (l + r) >> 1;
  Real x = points[mid].x;
  Real d = min(closestPair(points, l, mid), closestPair(points, mid, r));
  auto iti = points.begin(), itl = iti + l, itm = iti + mid, itr = iti + r;
  inplace_merge(itl, itm, itr, [](const Point& lhs, const Point& rhs) -> bool {
   return lessThan(lhs.y, rhs.y);
  });
  vector<Point> nearLine;
  for (int i = l; i < r; i++) {
   if (greaterThanOrEqual(fabs(points[i].x - x), d)) continue;
   int sz = nearLine.size();
   for (int j = sz - 1; j >= 0; j--) {
    Point dv = points[i] - nearLine[j];
    if (dv.y >= d) break;
    d = min(d, dv.abs());
   }
   nearLine.push_back(points[i]);
  }
  return d;
 }

 //-----------------------------------------------------------

 /// @brief 線分の交差数を数える
 int countIntersections(vector<Segment> segments) {
  struct Event {
   Real x;
   int type;  // 0:horizontal start,1:vertical,2:horizontal end
   Real y1, y2;
   Event(Real x, int type, Real y1, Real y2) : x(x), type(type), y1(y1), y2(y2) {}
   bool operator<(const Event& other) const {
    if (x == other.x) return type < other.type;
    return x < other.x;
   }
  };
  vector<Event> events;
  sort(segments.begin(), segments.end(), [](const Segment& lhs, const Segment& rhs) -> bool {
   return lessThan(min(lhs.a.x, lhs.b.x), min(rhs.a.x, rhs.b.x));
  });
  for (const auto& seg : segments) {
   if (seg.a.y == seg.b.y) {
    // Horizontal segment
    Real y = seg.a.y;
    Real x1 = min(seg.a.x, seg.b.x);
    Real x2 = max(seg.a.x, seg.b.x);
    events.emplace_back(x1, 0, y, y);
    events.emplace_back(x2, 2, y, y);
   } else {
    // Vertical segment
    Real x = seg.a.x;
    Real y1 = min(seg.a.y, seg.b.y);
    Real y2 = max(seg.a.y, seg.b.y);
    events.emplace_back(x, 1, y1, y2);
   }
  }
  sort(events.begin(), events.end());
  set<Real> activeSegments;
  int intersectionCount = 0;
  for (const auto& event : events) {
   if (event.type == 0) {
    // Add horizontal segment to active set
    activeSegments.insert(event.y1);
   } else if (event.type == 2) {
    // Remove horizontal segment from active set
    activeSegments.erase(event.y1);
   } else if (event.type == 1) {
    // Count intersections with vertical segment
    auto lower = activeSegments.lower_bound(event.y1);
    auto upper = activeSegments.upper_bound(event.y2);
    intersectionCount += distance(lower, upper);
   }
  }
  return intersectionCount;
 }

 //-----------------------------------------------------------

 /// @brief 2つの円の交点の個数を返す
 int countCirclesIntersection(const Circle& c1, const Circle& c2) {
  Real d =
   sqrt((c1.center.x - c2.center.x) * (c1.center.x - c2.center.x) +
     (c1.center.y - c2.center.y) * (c1.center.y - c2.center.y));
  Real r1 = c1.r, r2 = c2.r;
  if (greaterThan(d, r1 + r2))
   return 4;
  else if (almostEqual(d, r1 + r2))
   return 3;
  else if (greaterThan(d, fabs(r1 - r2)))
   return 2;
  else if (almostEqual(d, fabs(r1 - r2)))
   return 1;
  else
   return 0;
 }

 /// @brief 内接円を求める
 Circle getInCircle(const Point& A, const Point& B, const Point& C) {
  Real a = (B - C).abs();
  Real b = (A - C).abs();
  Real c = (A - B).abs();
  Real s = (a + b + c) / 2;
  Real area = sqrt(s * (s - a) * (s - b) * (s - c));
  Real r = area / s;
  Real cx = (a * A.x + b * B.x + c * C.x) / (a + b + c);
  Real cy = (a * A.y + b * B.y + c * C.y) / (a + b + c);
  return Circle{Point(cx, cy), r};
 }

 /// @brief 外接円を求める
 Circle getCircumCircle(const Point& A, const Point& B, const Point& C) {
  Real D = 2 * (A.x * (B.y - C.y) + B.x * (C.y - A.y) + C.x * (A.y - B.y));
  Real Ux = ((A.x * A.x + A.y * A.y) * (B.y - C.y) + (B.x * B.x + B.y * B.y) * (C.y - A.y) + (C.x * C.x + C.y * C.y) * (A.y - B.y)) / D;
  Real Uy = ((A.x * A.x + A.y * A.y) * (C.x - B.x) + (B.x * B.x + B.y * B.y) * (A.x - C.x) + (C.x * C.x + C.y * C.y) * (B.x - A.x)) / D;
  Point center(Ux, Uy);
  Real radius = (center - A).abs();
  return Circle{center, radius};
 }

 /// @brief 円と直線の交点を求める
 vector<Point> getCircleLineIntersection(const Circle& c, Point p1, Point p2) {
  Real cx = c.center.x, cy = c.center.y, r = c.r;
  Real dx = p2.x - p1.x;
  Real dy = p2.y - p1.y;
  Real a = dx * dx + dy * dy;
  Real b = 2 * (dx * (p1.x - cx) + dy * (p1.y - cy));
  Real c_const = (p1.x - cx) * (p1.x - cx) + (p1.y - cy) * (p1.y - cy) - r * r;
  Real discriminant = b * b - 4 * a * c_const;
  vector<Point> intersections;
  if (almostEqual(discriminant, 0)) {
   Real t = -b / (2 * a);
   Real ix = p1.x + t * dx;
   Real iy = p1.y + t * dy;
   intersections.emplace_back(ix, iy);
   intersections.emplace_back(ix, iy);
  } else if (discriminant > 0) {
   Real sqrt_discriminant = sqrt(discriminant);
   Real t1 = (-b + sqrt_discriminant) / (2 * a);
   Real t2 = (-b - sqrt_discriminant) / (2 * a);
   Real ix1 = p1.x + t1 * dx;
   Real iy1 = p1.y + t1 * dy;
   Real ix2 = p1.x + t2 * dx;
   Real iy2 = p1.y + t2 * dy;
   intersections.emplace_back(ix1, iy1);
   intersections.emplace_back(ix2, iy2);
  }
  if (almostEqual(intersections[0].x, intersections[1].x)) {
   if (greaterThan(intersections[0].y, intersections[1].y)) swap(intersections[0], intersections[1]);
  } else if (greaterThan(intersections[0].x, intersections[1].x)) {
   swap(intersections[0], intersections[1]);
  }
  return intersections;
 }

 /// @brief 2つの円の交点を求める
 vector<Point> getCirclesIntersect(const Circle& c1, const Circle& c2) {
  Real x1 = c1.center.x, y1 = c1.center.y, r1 = c1.r;
  Real x2 = c2.center.x, y2 = c2.center.y, r2 = c2.r;
  Real d = sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
  if (d > r1 + r2 || d < abs(r1 - r2)) return {};  // No intersection
  Real a = (r1 * r1 - r2 * r2 + d * d) / (2 * d);
  Real h = sqrt(r1 * r1 - a * a);
  Real x0 = x1 + a * (x2 - x1) / d;
  Real y0 = y1 + a * (y2 - y1) / d;
  Real rx = -(y2 - y1) * (h / d);
  Real ry = (x2 - x1) * (h / d);
  Point p1(x0 + rx, y0 + ry);
  Point p2(x0 - rx, y0 - ry);
  vector<Point> intersections;
  intersections.push_back(p1);
  intersections.push_back(p2);
  if (almostEqual(intersections[0].x, intersections[1].x)) {
   if (greaterThan(intersections[0].y, intersections[1].y)) swap(intersections[0], intersections[1]);
  } else if (greaterThan(intersections[0].x, intersections[1].x)) {
   swap(intersections[0], intersections[1]);
  }
  return intersections;
 }

 /// @brief 点から引ける円の接線の接点を求める
 vector<Point> getTangentLinesFromPoint(const Circle& c, const Point& p) {
  Real cx = c.center.x, cy = c.center.y, r = c.r;
  Real px = p.x, py = p.y;
  Real dx = px - cx;
  Real dy = py - cy;
  Real d = (p - c.center).abs();
  if (lessThan(d, r))
   return {};  // No tangents if the point is inside the circle
  else if (almostEqual(d, r))
   return {p};
  Real a = r * r / d;
  Real h = sqrt(r * r - a * a);
  Real cx1 = cx + a * dx / d;
  Real cy1 = cy + a * dy / d;
  vector<Point> tangents;
  tangents.emplace_back(cx1 + h * dy / d, cy1 - h * dx / d);
  tangents.emplace_back(cx1 - h * dy / d, cy1 + h * dx / d);
  if (almostEqual(tangents[0].x, tangents[1].x)) {
   if (greaterThan(tangents[0].y, tangents[1].y)) swap(tangents[0], tangents[1]);
  } else if (greaterThan(tangents[0].x, tangents[1].x)) {
   swap(tangents[0], tangents[1]);
  }
  return tangents;
 }

 /// @brief 2つの円の共通接線を求める
 vector<Segment> getCommonTangentsLine(const Circle& c1, const Circle& c2) {
  Real x1 = c1.center.x, y1 = c1.center.y, r1 = c1.r;
  Real x2 = c2.center.x, y2 = c2.center.y, r2 = c2.r;
  Real dx = x2 - x1;
  Real dy = y2 - y1;
  Real d = sqrt(dx * dx + dy * dy);
  vector<Segment> tangents;
  // Coincident circles(infinite tangents)
  if (almostEqual(d, 0) && almostEqual(r1, r2)) return tangents;
  // External tangents
  if (greaterThanOrEqual(d, r1 + r2)) {
   Real a = atan2(dy, dx);
   for (int sign : {-1, 1}) {
    Real theta = acos((r1 + r2) / d);
    Real cx1 = x1 + r1 * cos(a + sign * theta);
    Real cy1 = y1 + r1 * sin(a + sign * theta);
    Real cx2 = x2 + r2 * cos(a + sign * theta);
    Real cy2 = y2 + r2 * sin(a + sign * theta);
    tangents.emplace_back(Segment{Point(cx1, cy1), Point(cx2, cy2)});
    if (almostEqual(d, r1 + r2)) break;
   }
  }
  // Internal tangents
  if (greaterThanOrEqual(d, fabs(r1 - r2))) {
   Real a = atan2(dy, dx);
   for (int sign : {-1, 1}) {
    Real theta = acos((r1 - r2) / d);
    Real cx1 = x1 + r1 * cos(a + sign * theta);
    Real cy1 = y1 + r1 * sin(a + sign * theta);
    Real cx2 = x2 - r2 * cos(a + sign * theta);
    Real cy2 = y2 - r2 * sin(a + sign * theta);
    tangents.emplace_back(Segment{Point(cx1, cy1), Point(cx2, cy2)});
    if (almostEqual(d, fabs(r1 - r2))) break;
   }
  }
  sort(tangents.begin(), tangents.end(), [&](const Segment& s1, const Segment& s2) {
   if (almostEqual(s1.a.x, s2.a.x))
    return lessThan(s1.a.y, s2.a.y);
   else
    return lessThan(s1.a.x, s2.a.x);
  });
  return tangents;
 }
}
// --- End of include: ./../../akakoi_lib/geometry/geometry.cpp ---

using namespace Geometry;

void solve() {
  int n; cin >> n;
  vector<Point> P(n);
  rep(i, n) {
    Real x, y; cin >> x >> y;
    P[i] = {x, y};
  }
  auto H = convexHull(P, true);
  cout << H.size() << endl;
  for (auto [x, y] : H) {
    cout << x << " " << y << endl;
  }
}

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout << fixed << setprecision(0);
  solve();
  return 0;
}
accuracy: 1
time_ms: 80
memory_kb: 11252
final_rank: 15

code_snippet: #line 1 "convexHull.test.cpp"
#define PROBLEM "https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/4/CGL_4_A"
#line 1 "/home/zawatin/compro/bonsai-library/test/template.hpp"
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep2(i, n) for (ll i = 0; i < (n); i++) 
#define rep3(i, a, b) for(ll i = a; i < (b); i++)
#define overload(a, b, c, d, ...) d
#define rep(...) overload(__VA_ARGS__, rep3, rep2)(__VA_ARGS__)
#define all(a) begin(a), end(a)
#define sz(a) ssize(a)
bool chmin(auto& a, auto b) { return a > b ? a = b, 1 : 0; }
bool chmax(auto& a, auto b) { return a < b ? a = b, 1 : 0; }

#line 3 "convexHull.test.cpp"
//#include "../../src/geometry/geometryR2.hpp"
using Z = long long;
struct Point {
    Z x, y;
    auto operator<=>(const Point&) const = default;
};
Point operator-(const Point& p, const Point& q) {
    return {p.x-q.x, p.y-q.y};
}
bool Zero(Z v) {
    return v == 0;
}
bool Negative(Z v) {
    return v < 0;
}
bool Positive(Z v) {
    return v > 0;
}
bool Smaller(Z a, Z b) {
    return a < b;
}
Z Cross(Point p, Point q) {
    return p.x * q.y - p.y * q.x;
}
Z Dot(Point p, Point q) {
    return p.x * q.x + p.y * q.y;
}
Z NormSq(Point p) {
    return Dot(p, p);
}
enum RELATION {
    // p0 -> p1 -> p2
    ONLINE_FRONT = -2,
    CLOCKWISE,
    // p0 -> p2 -> p1
    ON_SEGMENT,
    COUNTER_CLOCKWISE,
    // p2 -> p0 -> p1 or p1 -> p0 -> p2
    ONLINE_BACK
};
// base point is p0
// note: very slow!, gosa yabai! (Z2 ha gosanasi)
RELATION Relation(const Point& p0, const Point& p1, const Point& p2) {
    Point a = p1 - p0, b = p2 - p0;
    if (!Zero(Cross(a, b)))
        return Positive(Cross(a, b)) ? COUNTER_CLOCKWISE : CLOCKWISE;
    return Negative(Dot(a, b)) ? ONLINE_BACK : (Smaller(NormSq(a), NormSq(b)) ? ONLINE_FRONT : ON_SEGMENT);
}

// You have to write Relation, Cross, Dot, and some operator override
// Copy from geometryR2
vector<Point> ConvexHull(vector<Point> p, bool strictly = true) {
    sort(p.begin(), p.end());
    p.erase(unique(p.begin(), p.end()), p.end());
    if (ssize(p) <= 2)
        return p;
    auto sweep = [&](auto first, auto last) {
        vector<Point> res;
        res.reserve(ssize(p));
        for (auto it = first ; it != last ; it++) {
            res.push_back(*it);
            while (ssize(res) >= 3) {
                auto rel = Relation(res[ssize(res)-3], res[ssize(res)-2], res[ssize(res)-1]);
                if (rel == COUNTER_CLOCKWISE)
                    break;
                if (!strictly and rel == ONLINE_FRONT)
                    break;
                swap(res[ssize(res)-2], res[ssize(res)-1]);
                res.pop_back();
            }
        }
        return res;
    };
    auto low = sweep(p.begin(), p.end()), up = sweep(p.rbegin(), p.rend());
    vector<Point> res;
    res.reserve(low.size()+up.size()-2);
    res.insert(res.end(), low.begin(), low.end());
    res.insert(res.end(), next(up.begin()), prev(up.end()));
    return res;
}
int main() {
    int n;
    cin >> n;
    vector<Point> P(n);
    for (Point& p : P)
        cin >> p.x >> p.y;
    auto ans = ConvexHull(P, false);
    ranges::rotate(ans, ranges::min_element(ans, [&](const auto& a, const auto& b) {
                    return a.y != b.y ? a.y < b.y : a.x < b.x;
                }));
    cout << ssize(ans) << '\n';
    for (auto p : ans)
        cout << p.x << ' ' << p.y << '\n';
}
accuracy: 1
time_ms: 20
memory_kb: 8320
final_rank: 4

code_snippet: #line 1 "convexHull.test.cpp"
#define PROBLEM "https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/4/CGL_4_A"
#line 1 "/home/zawatin/compro/bonsai-library/test/template.hpp"
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep2(i, n) for (ll i = 0; i < (n); i++) 
#define rep3(i, a, b) for(ll i = a; i < (b); i++)
#define overload(a, b, c, d, ...) d
#define rep(...) overload(__VA_ARGS__, rep3, rep2)(__VA_ARGS__)
#define all(a) begin(a), end(a)
#define sz(a) ssize(a)
bool chmin(auto& a, auto b) { return a > b ? a = b, 1 : 0; }
bool chmax(auto& a, auto b) { return a < b ? a = b, 1 : 0; }

#line 1 "/home/zawatin/compro/bonsai-library/src/geometry/geometryR2.hpp"
// Real 
using R = long double;

// CHECK THIS!!
const R EPS = 1e-12;
inline bool Positive(R v) {
    return v > EPS;
}
inline bool Negative(R v) {
    return v < -EPS;
}
inline bool Zero(R v) {
    return !Positive(v) and !Negative(v);
}
inline bool Smaller(R l, R r) {
    return Negative(l - r);
}
inline bool Bigger(R l, R r) {
    return Positive(l - r);
}
inline bool Equal(R l, R r) {
    return Zero(l - r);
}

inline R Square(R v) {
    return Zero(v) ? v : v * v;
}
inline R Sqrt(R v) {
    // kyokutan ni tiisai toki dake dame to suru
    // ABC426-E Closest Moment
    assert(v > -0.1);
    return sqrtl(max<R>(0, v));
}
inline R Abs(R v) {
    return Negative(v) ? -v : v;
}

// Angle

const R PI = acosl(-1);
const R TAU = 2 * PI;
R ArcToRad(R arc) {
    return (arc * PI) / 180.l;
}
R RadToArc(R rad) {
    return (rad * 180) / PI;
}

// Point

struct Point {
    R x, y;
};

Point operator+(const Point& p, const Point& q) {
    return {p.x + q.x, p.y + q.y};
}
Point operator-(const Point& p, const Point& q) {
    return {p.x - q.x, p.y - q.y};
}
Point operator-(const Point& p) {
    return {-p.x, -p.y};
}
Point operator*(const Point& p, R k) {
    return {p.x * k, p.y * k};
}
Point operator/(const Point& p, R k) {
    return {p.x / k, p.y / k};
}
bool operator<(const Point& p, const Point& q) {
    if (!Equal(p.x, q.x))
        return Smaller(p.x, q.x);
    return Smaller(p.y, q.y);
}
bool operator==(const Point& p, const Point& q) {
    return Equal(p.x, q.x) and Equal(p.y, q.y);
}

ostream& operator<<(ostream& os, const Point& p) {
    os << '(' << p.x << ',' << p.y << ')';
    return os; 
}
istream& operator>>(istream& is, Point& p) {
    is >> p.x >> p.y;
    return is;
}

R NormSq(const Point& p) {
    return Square(p.x) + Square(p.y);
}
R Norm(const Point& p) {
    return Sqrt(Norm(p));
}
Point Normalized(const Point& p) {
    return p / Sqrt(NormSq(p));
}
R DistSq(const Point& p, const Point& q) {
    return NormSq(p - q);
}
R Dist(const Point& p, const Point& q) {
    return Sqrt(DistSq(p, q));
}
R Dot(const Point& p, const Point& q) {
    return p.x * q.x + p.y * q.y;
}
R Cross(const Point& p, const Point& q) {
    return p.x * q.y - p.y * q.x;
}

Point Rotated(const Point& p, R rad) {
    return {p.x * cosl(rad) - p.y * sinl(rad), p.x * sinl(rad) + p.y * cosl(rad)};
}
Point Rot90(const Point& p) {
    return {-p.y, p.x};
}
R Argument(const Point& p) {
    return atan2l(p.y, p.x);
}

enum RELATION {
    // p0 -> p1 -> p2
    ONLINE_FRONT = -2,
    CLOCKWISE,
    // p0 -> p2 -> p1
    ON_SEGMENT,
    COUNTER_CLOCKWISE,
    // p2 -> p0 -> p1 or p1 -> p0 -> p2
    ONLINE_BACK
};
// base point is p0
// note: very slow!, gosa yabai! (Z2 ha gosanasi)
RELATION Relation(const Point& p0, const Point& p1, const Point& p2) {
    Point a = p1 - p0, b = p2 - p0;
    if (!Zero(Cross(a, b)))
        return Positive(Cross(a, b)) ? COUNTER_CLOCKWISE : CLOCKWISE;
    return Negative(Dot(a, b)) ? ONLINE_BACK : (Smaller(NormSq(a), NormSq(b)) ? ONLINE_FRONT : ON_SEGMENT);
}

enum ContainRest {
    INSIDE,
    ON,
    OUTSIDE
};

// Line
struct Line {
    Point a, b;
};

bool Parallel(const Line& a, const Line& b) {
    return Zero(Cross(a.b - a.a, b.b - b.a));
}
bool Orthogonal(const Line& a, const Line& b) {
    return Zero(Dot(a.b - a.a, b.b - b.a));
}

// Projection
Point Projection(const Point& p, const Line& l) {
    R coef = Dot(l.b - l.a, p - l.a) / DistSq(l.a, l.b);
    return l.b * coef + l.a * (1 - coef);
}
Point Reflection(const Point& p, const Line& l) {
    return Projection(p, l) * 2 - p;
}

// Segment
struct Segment {
    Point a, b;
    R lenSq() const {
        return DistSq(a, b);
    }
    R len() const {
        return Dist(a, b);
    }
    Point ab() const {
        return b - a;
    }
    Point ba() const {
        return a - b;
    }
};

bool Straddle(const Segment& s, const Segment& t) {
    return Relation(s.a, s.b, t.a) * Relation(s.a, s.b, t.b) <= 0;
}

bool Intersect(Segment s, Segment t) {
    return Straddle(s, t) and Straddle(t, s);
}

optional<Point> CrossPoint(Segment s, Segment t) {
    if (!Intersect(s, t))
        return nullopt;
    if (Zero(Cross(s.ab(), t.ab()))) {
        // |{Cross Point}} != 1 case (return arbitry one)
        // This case is not verified...
        for (int i = 0 ; i < 2 ; i++) {
            if (Negative(Dot(s.ab(), t.a - s.a)))
                return s.a;
            if (Negative(Dot(s.ab(), t.b - s.a)))
                return s.a;
            if (Negative(Dot(s.ba(), t.a - s.b)))
                return s.b;
            if (Negative(Dot(s.ba(), t.b - s.b)))
                return s.b;
            swap(s, t);
        }
        assert(0);
        return Point{};
    }
    else {
        // This part is verified
        R norm = Sqrt(NormSq(s.ab()));
        R r1 = abs(Cross(s.ab(), t.a - s.a)) / norm;
        R r2 = abs(Cross(s.ab(), t.b - s.a)) / norm;
        return t.a + t.ab() * (r1 / (r1 + r2));
    }
}

R Dist(const Point& p, const Segment& s) {
    if (Negative(Dot(s.ab(), p - s.a)))
        return Dist(p, s.a);
    if (Negative(Dot(s.ba(), p - s.b)))
        return Dist(p, s.b);
    return Abs(Cross(s.ab(), p - s.a)) / s.len();
}

R Dist(Segment s, Segment t) {
    if (Intersect(s, t))
        return 0;
    else
        return min({Dist(s.a, t), Dist(s.b, t), Dist(t.a, s), Dist(t.b, s)});
}

// Polygon, PointCloud
// note: ranges::sort, ranges::min_element, ... etc is not avaiable
using Poly = vector<Point>;
using PointCloud = vector<Point>;

R Area(const Poly& p) {
    R res = 0;
    for (int i = 1 ; i < ssize(p) ; i++)
        res += Cross(p[i] - p[0], p[(i+1)%ssize(p)] - p[0]);
    return res / 2;
}

bool IsConvex(const Poly& p) {
    assert(ssize(p) >= 3);
    for (int i = 0 ; i < ssize(p) ; i++)
        if (Relation(p[i], p[(i+1)%ssize(p)], p[(i+2)%ssize(p)]) == CLOCKWISE)
            return false;
    return true;
}

// not have to convex
ContainRest Contain(const Poly& poly, const Point& p) {
    int odd = 0;
    int n = ssize(poly);
    for (int i = 0 ; i < n ; i++) {
        if (poly[i] == p)
            return ON;
        if (Relation(poly[i], poly[(i+1)%n], p) == ON_SEGMENT)
            return ON;
        Point a = poly[i] - p, b = poly[(i+1)%n] - p;
        if (Bigger(a.y, b.y))
            swap(a, b);
        odd ^= !Positive(a.y) and Positive(b.y) and Positive(Cross(a, b));
    }
    return odd ? INSIDE : OUTSIDE;
}

Poly ConvexHull(PointCloud p, bool strictly = true) {
    sort(p.begin(), p.end());
    p.erase(unique(p.begin(), p.end()), p.end());
    if (ssize(p) <= 2)
        return Poly{p};
    auto sweep = [&](auto first, auto last) -> PointCloud {
        PointCloud res;
        res.reserve(ssize(p));
        for (auto it = first ; it != last ; it++) {
            res.push_back(*it);
            while (ssize(res) >= 3) {
                auto rel = Relation(res[ssize(res)-3], res[ssize(res)-2], res[ssize(res)-1]);
                if (rel == COUNTER_CLOCKWISE)
                    break;
                if (!strictly and rel == ONLINE_FRONT)
                    break;
                swap(res[ssize(res)-2], res[ssize(res)-1]);
                res.pop_back();
            }
        }
        return res;
    };
    auto low = sweep(p.begin(), p.end()), up = sweep(p.rbegin(), p.rend());
    Poly res;
    res.reserve(low.size()+up.size()-2);
    res.insert(res.end(), low.begin(), low.end());
    res.insert(res.end(), next(up.begin()), prev(up.end()));
    return res;
}

// Circle
struct Circle {
    Point p;
    R r;
};

optional<Circle> CircumscribedCircle(Point p, Point q, Point r) {
    if (Zero(Cross(q - p, r - p)))
        return nullopt;
    auto bisec = [&](Point a, Point b) {
        Point m = (a + b) / 2, v = Rot90(b - a);
        return pair{m, m + v};
    };
    auto l0 = bisec(p, q), l1 = bisec(p, r);
    // This is CrossPointLine
    Point c = l0.first + (l0.second - l0.first) * 
        (Cross(l1.first - l0.first, l1.second - l1.first) / Cross(l0.second - l0.first, l1.second - l1.first));
    R rad = Sqrt(DistSq(p, c));
    return Circle{c, rad};
}

//optional<Circle> InCircle(Point p, Point q, Point r) {
//    if (Zero(Cross(q - p, r - p)))
//        return nullopt;
//    auto bisec = [&](Point a, Point b, Point c) -> pair<Point, Point> {
//        Point ba = b - a, ca = c - a;
//        Point op = a + Rotated((Argument(ca) - Arugment(ba)) / 2);
//        return {a, op};
//    };
//    auto l0 = bisec(p, q, r), l1 = bisec(q, p, r);
//    Point c = l0.first + (l0.second - l0.first) * 
//        (Cross(l1.first - l0.first, l1.second - l1.first) / Cross(l0.second - l0.first, l1.second - l1.first));
//}

int NumberCommonTangent(const Circle& a, const Circle& b) {
    R d = DistSq(a.p, b.p);
    R down = Square(abs(a.r - b.r));
    if (Smaller(d, down))
        return 0;
    if (Equal(d, down))
        return 1;
    R up = Square(a.r + b.r);
    if (Smaller(d, up))
        return 2;
    if (Equal(d, up))
        return 3;
    return 4;
}

optional<pair<Point, Point>> CrossPoint(Circle a, Circle b) {
    int n = NumberCommonTangent(a, b);
    if (n == 0 or n == 4)
        return nullopt;
    if (Bigger(a.r, b.r))
        swap(a, b);
    if (Zero(a.r))
        return pair{a.p, b.p};
    R d = Sqrt(DistSq(a.p, b.p));
    R cosin = (Square(a.r) + Square(d) - Square(b.r)) / (2*a.r*d);
    R rc = a.r*cosin, rs = Sqrt(Square(a.r) - Square(rc));
    Point lr = Normalized(b.p - a.p), h = a.p + lr * rc;
    Point p = h + Rot90(lr) * rs, q = h + Point{lr.y, -lr.x} * rs;
    return pair{p, q};
}
#line 4 "convexHull.test.cpp"
int main() {
    int n;
    cin >> n;
    vector<Point> P(n);
    for (Point& p : P)
        cin >> p;
    auto ans = ConvexHull(P, false);
    ranges::rotate(ans, ranges::min_element(ans, [&](const auto& a, const auto& b) {
                    if (!Equal(a.y, b.y))
                        return Smaller(a.y, b.y);
                    return Smaller(a.x, b.y);
                }));
    cout << ssize(ans) << '\n';
    for (auto p : ans)
        cout << p.x << ' ' << p.y << '\n';
}
accuracy: 0.9583333333333334
time_ms: 70
memory_kb: 13460
final_rank: 20

code_snippet: #include <bits/stdc++.h>
using namespace std;

bool to_left(pair<int, int> a, pair<int, int> b, pair<int, int> c) {
	// Cambiar a >= si se quiere incluir los puntos en los bordes también :v
	return 1ll * (b.first - a.first) * (c.second - b.second) >= 1ll * (c.first - b.first) * (b.second - a.second);
}

vector<pair<int, int>> get_lower_hull(vector<pair<int, int>> P) {
	vector<pair<int, int>> CH;
	for(auto e : P) {
		while(CH.size() >= 2 and !to_left(CH[(int)CH.size() - 2], CH[(int)CH.size() - 1], e)) {
			CH.pop_back();
		}
		CH.emplace_back(e);
	}
	return CH;
}

vector<pair<int, int>> convex_hull(vector<pair<int, int>> P) {
	sort(P.begin(), P.end());
	vector<pair<int, int>> lower = get_lower_hull(P);
	reverse(P.begin(), P.end());
	vector<pair<int, int>> upper = get_lower_hull(P);
	for(int i = 1; i + 1 < upper.size(); i++) {
		lower.emplace_back(upper[i]);
	}
	return lower;
}

int main() {
	cin.tie(0) -> sync_with_stdio(false);
	int n;
	cin >> n;
	vector<pair<int, int>> P(n);
	for(int i = 0; i < n; i++) cin >> P[i].first >> P[i].second;
	vector<pair<int, int>> CH = convex_hull(P);
	cout << CH.size() << '\n';
	int at = 0;
	for(int i = 1; i < CH.size(); i++) {
		if(CH[at].second > CH[i].second) {
			at = i;
		}
		else if(CH[at].second == CH[i].second and CH[at].first > CH[i].first) {
			at = i;
		}
	}
	for(int i = 0; i < CH.size(); i++) {
		int to = (at + i) % CH.size();
		cout << CH[to].first << " " << CH[to].second << '\n';
	}
	return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 6032
final_rank: 3

code_snippet: // https://tubo28.me/compprog/algorithm/ より.

#include <bits/stdc++.h>
// #define int int64_t

#define FOR(i, a, b) for (int i = (a); i < int(b); ++i)
#define RFOR(i, a, b) for (int i = (b)-1; i >= int(a); --i)
#define rep(i, n) FOR(i, 0, n)
#define rep1(i, n) FOR(i, 1, int(n) + 1)
#define rrep(i, n) RFOR(i, 0, n)
#define rrep1(i, n) RFOR(i, 1, int(n) + 1)
#define all(c) begin(c), end(c)
namespace io {
#ifdef LOCAL
#define dump(...)                                         \
    do {                                                  \
        std::ostringstream os;                            \
        os << __LINE__ << ":\t" << #__VA_ARGS__ << " = "; \
        io::print_to(os, ", ", "\n", __VA_ARGS__);        \
        std::cerr << io::highlight(os.str());             \
    } while (0)
#define dump_(cntnr)                                 \
    do {                                             \
        std::ostringstream os;                       \
        os << __LINE__ << ":\t" << #cntnr << " = ["; \
        io::print_to_(os, ", ", "]\n", all(cntnr));  \
        std::cerr << io::highlight(os.str());        \
    } while (0)
#define dumpf(fmt, ...)                             \
    do {                                            \
        const int N = 4096;                         \
        auto b = new char[N];                       \
        int l = snprintf(b, N, "%d:\t", __LINE__);  \
        snprintf(b + l, N - l, fmt, ##__VA_ARGS__); \
        std::cerr << io::highlight(b) << std::endl; \
        delete[] b;                                 \
    } while (0)
#else
#define dump(...)
#define dump_(...)
#define dumpf(...)
#endif
std::string highlight(std::string s) {
#ifdef _MSC_VER
    return s;
#else
    return "\033[33m" + s + "\033[0m";
#endif
}
template <typename T>
void print_to(std::ostream &os, std::string, std::string tail, const T &first) {
    os << first << tail;
}
template <typename F, typename... R>
void print_to(std::ostream &os, std::string del, std::string tail, const F &first,
              const R &... rest) {
    os << first << del;
    print_to(os, del, tail, rest...);
}
template <typename I>
void print_to_(std::ostream &os, std::string del, std::string tail, I begin, I end) {
    for (I it = begin; it != end;) {
        os << *it;
        os << (++it != end ? del : tail);
    }
}
template <typename F, typename... R>
void println(const F &first, const R &... rest) {
    print_to(std::cout, "\n", "\n", first, rest...);
}
template <typename F, typename... R>
void print(const F &first, const R &... rest) {
    print_to(std::cout, " ", "\n", first, rest...);
}
template <typename I>
void println_(I begin, I end) {
    print_to_(std::cout, "\n", "\n", begin, end);
}
template <typename I>
void print_(I begin, I end) {
    print_to_(std::cout, " ", "\n", begin, end);
}
template <typename C>
void println_(const C &cntnr) {
    println_(std::begin(cntnr), std::end(cntnr));
}
template <typename C>
void print_(const C &cntnr) {
    print_(std::begin(cntnr), std::end(cntnr));
}
int _ = (
#ifndef LOCAL
    std::cin.tie(nullptr), std::ios::sync_with_stdio(false),
#endif
    std::cout.precision(10), std::cout.setf(std::ios::fixed));
}
using io::print;
using io::println;
using io::println_;
using io::print_;
template <typename T>
using vec = std::vector<T>;
using vi = vec<int>;
using vvi = vec<vi>;
using pii = std::pair<int, int>;
using ll = long long;
using ld = long double;
using namespace std;
// const int MOD = 1000000007;




using ld = long double;
using P = std::complex<ld>;
using G = std::vector<P>;
const ld pi = std::acos(-1);
const ld eps = 1e-10;
const ld inf = 1e12;

ld cross(const P &a, const P &b) { return a.real() * b.imag() - a.imag() * b.real(); }
ld dot(const P &a, const P &b) { return a.real() * b.real() + a.imag() * b.imag(); }

/*
                    CCW

 -- BEHIND -- [a -- ON -- b] --- FRONT --

                    CW
 */
enum CCW_RESULT { CCW = +1, CW = -1, BEHIND = +2, FRONT = -2, ON = 0 };
int ccw(P a, P b, P c) {
    b -= a;
    c -= a;
    if (cross(b, c) > eps) return CCW;    // counter clockwise
    if (cross(b, c) < -eps) return CW;    // clockwise
    if (dot(b, c) < 0) return BEHIND;     // c--a--b on line
    if (norm(b) < norm(c)) return FRONT;  // a--b--c on line
    return ON;
}

namespace std {
bool operator<(const P &a, const P &b) {
    return std::abs(real(a) - real(b)) > eps ? real(a) < real(b) : imag(a) < imag(b);
}
}

struct L : public std::vector<P> {
    L(const P &a = P(), const P &b = P()) : std::vector<P>(2) {
        begin()[0] = a;
        begin()[1] = b;
    }

    // Ax + By + C = 0
    L(ld A, ld B, ld C) {
        if (std::abs(A) < eps && std::abs(B) < eps) {
            abort();
        } else if (std::abs(A) < eps) {
            *this = L(P(0, -C / B), P(1, -C / B));
        } else if (std::abs(B) < eps) {
            *this = L(P(-C / A, 0), P(-C / A, 1));
        } else {
            *this = L(P(0, -C / B), P(-C / A, 0));
        }
    }
};

struct C {
    P p;
    ld r;
    C(const P &p = 0, ld r = 0) : p(p), r(r) {}
};




bool intersectLL(const L &l, const L &m) {
    return std::abs(cross(l[1] - l[0], m[1] - m[0])) > eps ||  // non-parallel
           std::abs(cross(l[1] - l[0], m[0] - l[0])) < eps;    // same line
}
bool intersectLS(const L &l, const L &s) {
    return cross(l[1] - l[0], s[0] - l[0]) *  // s[0] is left of l
               cross(l[1] - l[0], s[1] - l[0]) <
           eps;  // s[1] is right of l
}
bool intersectLP(const L &l, const P &p) { return std::abs(cross(l[1] - p, l[0] - p)) < eps; }
bool intersectSS(const L &s, const L &t) {
    return ccw(s[0], s[1], t[0]) * ccw(s[0], s[1], t[1]) <= 0 &&
           ccw(t[0], t[1], s[0]) * ccw(t[0], t[1], s[1]) <= 0;
}
bool intersectSP(const L &s, const P &p) {
    return std::abs(s[0] - p) + std::abs(s[1] - p) - std::abs(s[1] - s[0]) <
           eps;  // triangle inequality
}

P projection(const L &l, const P &p) {
    ld t = dot(p - l[0], l[0] - l[1]) / norm(l[0] - l[1]);
    return l[0] + t * (l[0] - l[1]);
}
P reflection(const L &l, const P &p) { return p + (ld)2 * (projection(l, p) - p); }
ld distanceLP(const L &l, const P &p) { return std::abs(p - projection(l, p)); }
ld distanceLL(const L &l, const L &m) { return intersectLL(l, m) ? 0 : distanceLP(l, m[0]); }
ld distanceLS(const L &l, const L &s) {
    if (intersectLS(l, s)) return 0;
    return std::min(distanceLP(l, s[0]), distanceLP(l, s[1]));
}
ld distanceSP(const L &s, const P &p) {
    const P r = projection(s, p);
    if (intersectSP(s, r)) return std::abs(r - p);
    return std::min(std::abs(s[0] - p), std::abs(s[1] - p));
}
ld distanceSS(const L &s, const L &t) {
    if (intersectSS(s, t)) return 0;
    return std::min(std::min(distanceSP(s, t[0]), distanceSP(s, t[1])),
                    std::min(distanceSP(t, s[0]), distanceSP(t, s[1])));
}
P crosspointLL(const L &l, const L &m) {
    ld A = cross(l[1] - l[0], m[1] - m[0]);
    ld B = cross(l[1] - l[0], l[1] - m[0]);
    if (std::abs(A) < eps && std::abs(B) < eps) return m[0];  // same line
    if (std::abs(A) < eps) assert(false);                     // !!!PRECONDITION NOT SATISFIED!!!
    return m[0] + B / A * (m[1] - m[0]);
}


// 符号付二倍面積 abs(area2(G pol))/2.0 で求められる.
// polは頂点反時計回りで渡す
ld area2(const G& pol) {
    ld s = 0;
    int n = pol.size();
    for (int i = 0; i < n; ++i) s += cross(pol[i], pol[(i + 1) % n]);
    return s;
}


// 凸包
// == CWを != CCWに置換すると 退化した角を除く.
G andrewScan(G ps) {
    int N = ps.size(), k = 0;
    std::sort(ps.begin(), ps.end());
    G res(N * 2);
    for (int i = 0; i < N; i++) {
        while (k >= 2 && ccw(res[k - 2], res[k - 1], ps[i]) == CW) k--;
        res[k++] = ps[i];
    }
    int t = k + 1;
    for (int i = N - 2; i >= 0; i--) {
        while (k >= t && ccw(res[k - 2], res[k - 1], ps[i]) == CW) k--;
        res[k++] = ps[i];
    }
    res.resize(k - 1);
    return res;
}

// P:点 point x: .real() y: .imag()
// L:直線 lineF
// S:線分 section
// G:多角形 vector<P>

signed main() {
    int N;
    cin >> N;
    G g(N);
    rep(i, N){
        int xi, yi;
        cin >> xi >> yi;
        g[i] = P(xi, yi);
    }
    g = andrewScan(g);
    cout << g.size() << endl;
    int startidx = 0;
    rep(i, g.size())if(g[startidx].imag()>g[i].imag()||(g[startidx].imag()==g[i].imag()&&g[startidx].real()>g[i].real())) startidx = i;
    rep(i, g.size())cout << (int)g[(i+startidx)%g.size()].real() << " " << (int)g[(i+startidx)%g.size()].imag() << endl;
    return 0;
}
accuracy: 1
time_ms: 70
memory_kb: 15452
final_rank: 18

code_snippet: #include <bits/stdc++.h>
#define T double

constexpr double eps = 1e-8, pi = std::acos(-1);

struct Point
{
	T x, y;
	Point(const T &X = {}, const T &Y = {}) : x(X), y(Y) {}
};
struct Line
{
	Point x, y;
	Line(const Point &X = {}, const Point &Y = {}) : x(X), y(Y) {}
};

inline int dcmp(const T &a)
{
	return a < -eps ? -1 : a > eps;
}
inline bool operator==(const Point &a, const Point &b)
{
	return !dcmp(a.x - b.x) && !dcmp(a.y - b.y);
}
inline double len(const Point &a)
{
	return std::hypot(a.x, a.y);
}
inline std::istream &operator>>(std::istream &is, Point &a)
{
	return is >> a.x >> a.y;
}
inline std::ostream &operator<<(std::ostream &os, const Point &a)
{
	return os << a.x << " " << a.y;
}
inline Point operator+(const Point &a, const Point &b)
{
	return {a.x + b.x, a.y + b.y};
}
inline Point operator-(const Point &a, const Point &b)
{
	return {a.x - b.x, a.y - b.y};
}
inline Point operator*(const Point &a, const T &b)
{
	return {a.x * b, a.y * b};
}
inline Point operator/(const Point &a, const T &b)
{
	return {a.x / b, a.y / b};
}
inline T dot(const Point &a, const Point &b)
{
	return {a.x * b.x + a.y * b.y};
}
inline T cross(const Point &a, const Point &b)
{
	return {a.x * b.y - a.y * b.x};
}
inline Point ratate90(const Point &a)
{
	return {-a.y, a.x};
}
inline Point ratate(const Point &a, double b)
{
	return {a.x * std::cos(b) - a.y * std::sin(b), a.x * std::sin(b) + a.y * std::cos(b)};
}
double angle(const Point &a, const Point &b)
{
	return std::acos(dot(a, b) / len(a) / len(b));
	return std::acos(dot(a, b) / len(a) / len(b)) / pi * 180;
}
Point p_ver_l(const Point &p, const Line &l)
{
	return l.x + (l.y - l.x) * dot(l.y - l.x, p - l.x) / dot(l.y - l.x, l.y - l.x);
}
Point l_inter_l(const Line &l1, const Line &l2)
{
	return l1.x + (l1.y - l1.x) * cross(l1.x - l2.x, l2.y - l2.x) / cross(l2.y - l2.x, l1.y - l1.x);
}
int p_rela_l(const Point &p, const Line &l)
{
	return dcmp(cross(l.y - l.x, p - l.x));
}
int l_rela_l(const Line &l1, const Line &l2)
{
	if (dcmp(cross(l1.y - l1.x, l2.y - l2.x)) == 0) return p_rela_l(l1.x, l2) != 0;
	return -1;
}
bool p_on_s(const Point &p, const Line &s)
{
	return p_rela_l(p, s) == 0 && dcmp(dot(s.y - p, s.x - p)) <= 0;
}
bool l_inter_s(const Line &l, const Line &s)
{
	return dcmp(cross(l.y - l.x, s.x - l.x)) * dcmp(cross(l.y - l.x, s.y - l.x)) <= 0;
}
bool s_inter_s(const Line &s1, const Line &s2)
{
	if (p_on_s(s1.x, s2) || p_on_s(s1.y, s2) || p_on_s(s2.x, s1) || p_on_s(s2.y, s1)) return 1;
	if (dcmp(cross(s1.y - s1.x, s2.x - s1.x)) * dcmp(cross(s1.y - s1.x, s2.y - s1.x)) >= 0) return 0;
	if (dcmp(cross(s2.y - s2.x, s1.x - s2.x)) * dcmp(cross(s2.y - s2.x, s1.y - s2.x)) >= 0) return 0;
	return 1;
}
double p_to_l(const Point &p, const Line &l)
{
	return std::abs(cross(l.y - l.x, p - l.x)) / len(l.y - l.x);
}
double p_to_s(const Point &p, const Line &s)
{
	if (dcmp(dot(s.y - s.x, p - s.x)) <= 0) return len(p - s.x);
	if (dcmp(dot(s.x - s.y, p - s.y)) <= 0) return len(p - s.y);
	return p_to_l(p, s);
}
T area(const std::vector<Point> &ps)
{
	T ar = 0;
	for (int i = 0, ie = ps.size(); i < ie; ++i)
	{
		ar += cross(ps[i], ps[(i + 1) % ie]);
	}
	return ar / 2;
}
bool Convex(const std::vector<Point> &ps, int pd)
{
	for (int i = 0, ie = ps.size(); i < ie; ++i)
	{
		if (dcmp(cross(ps[i] - ps[(i + 1) % ie], ps[(i + 1) % ie] - ps[(i + 2) % ie])) == pd) return 0;
	}
	return 1;
}

std::vector<Point> Andrew(std::vector<Point> ps)
{
	int r = 0;
	std::vector<Point> sp(ps.size() + 1);
	std::sort(ps.begin(), ps.end(), [](Point &a, Point &b)
	{
		return dcmp(a.y - b.y) ? dcmp(a.y - b.y) < 0 : dcmp(a.x - b.x) < 0;
	});
	
	for (int i = 0, ie = ps.size(); i < ie; ++i)
	{
		while (r > 1 && cross(sp[r - 1] - sp[r - 2], ps[i] - sp[r - 2]) < 0) --r;
		sp[r++] = ps[i];
	}
	
	for (int i = ps.size() - 2, j = r; i >= 0; --i)
	{
		while (r > j && cross(sp[r - 1] - sp[r - 2], ps[i] - sp[r - 2]) < 0) --r;
		sp[r++] = ps[i];
	}
	
	sp.resize(r - 1);
	
	return sp;
}

void solve()
{
	int n;
	
	std::cin >> n;
	std::vector<Point> ps(n);
	
	for (int i = 0; i < n; ++i)
	{
		std::cin >> ps[i];
	}
	
	std::vector<Point> chull = Andrew(ps);
	
	std::cout << chull.size() << "\n";
	
//	std::sort(chull.begin(), chull.end(), [](Point &a, Point &b)
//	{
//		return dcmp(a.y - b.y) ? dcmp(a.y - b.y) < 0 : dcmp(a.x - b.x) < 0;
//	});


	for (int i = 0, ie = chull.size(); i < ie; ++i)
	{
		std::cout << chull[i] << "\n";
	}

}

signed main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout << std::fixed << std::setprecision(0);

	int _ = 1;
//	std::cin >> _;

	while (_--)
	{


		solve();
	}

	return 0;
}
accuracy: 1
time_ms: 60
memory_kb: 7860
final_rank: 10

code_snippet: #include <bits/stdc++.h>
using namespace std;

#define x real()
#define y imag()

using ll = long long;
using pt = complex<ll>;

ll cross(pt a, pt b) { return imag(conj(a) * b); }

pt read() {
    int p,q;
    cin >> p >> q;
    return pt(p, q);
}

bool is_line(vector<pt> a) {
    const int n = a.size();
    pt p0 = a[0];
    for (int i = 1; i + 1 < n; i++) {
        if (cross(a[i] - p0, a[i + 1] - p0) != 0) return 0;
    }
    return 1;
}

vector<pt> convex_hull(vector<pt> a) { // include colinear
    const int n = a.size();
    if (n == 1) return a;
    int idx = 0;
    for (int i = 1; i < n; i++) {
        if (make_pair(a[i].y, a[i].x) < make_pair(a[idx].y, a[idx].x)) {
            idx = i;
        }
    }

    pt p0 = a[idx];
    
    a.erase(a.begin() + idx);
    sort(a.begin(), a.end(), [&](pt A, pt B) {
        int o = cross(A - p0, B - p0);
        if (o == 0) return norm(A - p0) < norm(B - p0);
        return o > 0;
    });
    a.insert(a.begin(), p0);

    if (is_line(a)) return a;

    // last line edge case
    int j = n - 2;
    while(j >= 0 and cross(a[n - 1] - p0, a[j] - p0) == 0) {
        j--;
    }
    reverse(a.begin() + j + 1, a.end());

    // building convex hull
    vector<pt> st = {a[0], a[1]};
    for (int i = 2; i < n; i++) {
        pt B = a[i];
        while(true) {
            pt A = st.back(); st.pop_back();
            pt C = st.back();
            if (cross(B - A, C - A) >= 0) {
                st.push_back(A);
                st.push_back(B);
                break;
            }
        }
    }

    return st;
}


void solve() {
    int n; cin >> n;
    vector<pt> a(n);
    for (int i = 0; i < n; i++) a[i] = read();
    vector<pt> ch = convex_hull(a);
    cout << ch.size() << '\n';
    for (auto p : ch) {
        cout << p.x << " " << p.y << '\n';
    }
}

int main() {
    cin.tie(0) -> sync_with_stdio(0);
    solve();
    return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 8744
final_rank: 5

code_snippet: #include <bits/stdc++.h>
using namespace std;

#define x real()
#define y imag()

using ll = long long;
using Point = complex<ll>;

ll cross(Point a, Point b) { return imag(conj(a) * b); }

Point read() {
    int p,q;
    cin >> p >> q;
    return Point(p, q);
}

void solve(int n) {
    vector<Point> a(n);
    int idx = -1;
    for (int i = 0; i < n; i++) {
        a[i] = read();
        if (idx == -1 or make_pair(a[i].y, a[i].x) < make_pair(a[idx].y, a[idx].x)) {
            idx = i;
        }
    }

    if (n == 1) {
        cout << a[0].x << " " << a[0].y << '\n';
        return;
    }


    Point p0 = a[idx];
    a.erase(a.begin() + idx);
    sort(a.begin(), a.end(), [&](Point A, Point B) {
        if (cross(A - p0, B - p0) == 0) return norm(A - p0) < norm(B - p0);
        return cross(A - p0, B - p0) > 0;
    });
    a.insert(a.begin(), p0);

    
    {
        int j = n - 2;
        while(j >= 0 and cross(a[n - 1] - p0, a[j] - p0) == 0) {
            j--;
        }
        reverse(a.begin() + j + 1, a.end());
    }

    vector<Point> st = {a[0], a[1]};
    for (int i = 2; i < n; i++) {
        Point B = a[i];
        while(true) {
            Point A = st.back(); st.pop_back();
            Point C = st.back();
            if (cross(B - A, C - A) >= 0) {
                st.push_back(A);
                st.push_back(B);
                break;
            }
        }
    }

    cout << st.size() << '\n';
    for (auto P : st) {
        cout << P.x << ' '<< P.y << '\n';
    }

}

int main() {
    cin.tie(0) -> sync_with_stdio(0);
    int n;
    while(cin >> n) {
        solve(n);
    }
    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 6324
final_rank: 1

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i,n) for (int i = 0; i< (n); ++i)
#define repi(i, a, b) for (int i = (a); i < (b); ++i)
#define all(x) (x).begin(), (x).end()
#define fore(i, a) for(auto &i:a)
using ll = long long;
using int64 = long long;
#define DEBUG(x) cerr << #x << ": "; for (auto _ : x) cerr << _ << " "; cerr << endl;
const int64 infll = (1LL << 62) - 1;
const int inf = (1 << 30) - 1;

struct IoSetup {
	IoSetup() {
		cin.tie(nullptr);
		ios::sync_with_stdio(false);
		cout << fixed << setprecision(10);
		cerr << fixed << setprecision(10);
	}
} iosetup;

template <typename T1, typename T2>
ostream& operator<<(ostream& os, const pair<T1, T2>& p) {
	os << p.first << " " << p.second;
	return os;
}

template <typename T1, typename T2>
istream& operator>>(istream& is, pair<T1, T2>& p) {
	is >> p.first >> p.second;
	return is;
}

template <typename T>
ostream& operator<<(ostream& os, const vector<T>& v) {
	for (int i = 0; i < (int)v.size(); i++) {
		os << v[i] << (i + 1 != v.size() ? " " : "");
	}
	return os;
}

template <typename T>
istream& operator>>(istream& is, vector<T>& v) {
	for (T& in : v) is >> in;
	return is;
}

template <typename T1, typename T2>
inline bool chmax(T1& a, T2 b) {
	return a < b && (a = b, true);
}

template <typename T1, typename T2>
inline bool chmin(T1& a, T2 b) {
	return a > b && (a = b, true);
}

template <typename T = int64>
vector<T> make_v(size_t a) {
	return vector<T>(a);
}

template <typename T, typename... Ts>
auto make_v(size_t a, Ts... ts) {
	return vector<decltype(make_v<T>(ts...))>(a, make_v<T>(ts...));
}

template <typename T, typename V>
typename enable_if<is_class<T>::value == 0>::type fill_v(T& t, const V& v) {
	t = v;
}

template <typename T, typename V>
typename enable_if<is_class<T>::value != 0>::type fill_v(T& t, const V& v) {
	for (auto& e : t) fill_v(e, v);
}

template <typename F>
struct FixPoint : F {
	explicit FixPoint(F&& f) : F(std::forward<F>(f)) {}

	template <typename... Args>
		decltype(auto) operator()(Args&&... args) const {
			return F::operator()(*this, std::forward<Args>(args)...);
		}
};

template <typename F>
inline decltype(auto) MFP(F&& f) {
	return FixPoint<F>{std::forward<F>(f)};
}
//Geometry
namespace geometry {
using Real = double;
const Real EPS = 1e-15;
const Real PI = acos(static_cast<Real>(-1));

enum { OUT, ON, IN };

inline int sign(const Real& r) { return r <= -EPS ? -1 : r >= EPS ? 1 : 0; }

inline bool equals(const Real& a, const Real& b) { return sign(a - b) == 0; }
}  // namespace geometry

namespace geometry {
using Point = complex<Real>;

istream& operator>>(istream& is, Point& p) {
  Real a, b;
  is >> a >> b;
  p = Point(a, b);
  return is;
}

ostream& operator<<(ostream& os, const Point& p) {
  return os << real(p) << " " << imag(p);
}

Point operator*(const Point& p, const Real& d) {
  return Point(real(p) * d, imag(p) * d);
}

// rotate point p counterclockwise by theta rad
Point rotate(Real theta, const Point& p) {
  return Point(cos(theta) * real(p) - sin(theta) * imag(p),
               sin(theta) * real(p) + cos(theta) * imag(p));
}

Real cross(const Point& a, const Point& b) {
  return real(a) * imag(b) - imag(a) * real(b);
}

Real dot(const Point& a, const Point& b) {
  return real(a) * real(b) + imag(a) * imag(b);
}

bool compare_x(const Point& a, const Point& b) {
  return equals(real(a), real(b)) ? imag(a) < imag(b) : real(a) < real(b);
}

bool compare_y(const Point& a, const Point& b) {
  return equals(imag(a), imag(b)) ? real(a) < real(b) : imag(a) < imag(b);
}

using Points = vector<Point>;
}  // namespace geometry

namespace geometry {
struct Line {
  Point a, b;

  Line() = default;

  Line(const Point& a, const Point& b) : a(a), b(b) {}

  Line(const Real& A, const Real& B, const Real& C) {  // Ax+By=C
    if (equals(A, 0)) {
      assert(!equals(B, 0));
      a = Point(0, C / B);
      b = Point(1, C / B);
    } else if (equals(B, 0)) {
      a = Point(C / A, 0);
      b = Point(C / A, 1);
    } else if (equals(C, 0)) {
      a = Point(0, C / B);
      b = Point(1, (C - A) / B);
    } else {
      a = Point(0, C / B);
      b = Point(C / A, 0);
    }
  }

  friend ostream& operator<<(ostream& os, Line& l) {
    return os << l.a << " to " << l.b;
  }

  friend istream& operator>>(istream& is, Line& l) { return is >> l.a >> l.b; }
};

using Lines = vector<Line>;
}  // namespace geometry

namespace geometry {
struct Segment : Line {
  Segment() = default;

  using Line::Line;
};

using Segments = vector<Segment>;
}  // namespace geometry
namespace geometry {
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_1_A
Point projection(const Line& l, const Point& p) {
  auto t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
  return l.a + (l.a - l.b) * t;
}
}  // namespace geometry

namespace geometry {
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_1_B
Point reflection(const Line& l, const Point& p) {
  return p + (projection(l, p) - p) * 2;
}
}  // namespace geometry

namespace geometry {
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_1_C
constexpr int COUNTER_CLOCKWISE = +1;
constexpr int CLOCKWISE = -1;
constexpr int ONLINE_BACK = +2;   // c-a-b
constexpr int ONLINE_FRONT = -2;  // a-b-c
constexpr int ON_SEGMENT = 0;     // a-c-b
int ccw(const Point& a, Point b, Point c) {
  b = b - a, c = c - a;
  if (sign(cross(b, c)) == +1) return COUNTER_CLOCKWISE;
  if (sign(cross(b, c)) == -1) return CLOCKWISE;
  if (sign(dot(b, c)) == -1) return ONLINE_BACK;
  if (norm(b) < norm(c)) return ONLINE_FRONT;
  return ON_SEGMENT;
}
}  // namespace geometry
namespace geometry {
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_A
bool is_orthogonal(const Line& a, const Line& b) {
  return equals(dot(a.a - a.b, b.a - b.b), 0.0);
}
}  // namespace geometry
namespace geometry {
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_A
bool is_parallel(const Line& a, const Line& b) {
  return equals(cross(a.b - a.a, b.b - b.a), 0.0);
}
}  // namespace geometry

namespace geometry {
bool is_intersect_ss(const Segment& s, const Segment& t) {
  return ccw(s.a, s.b, t.a) * ccw(s.a, s.b, t.b) <= 0 &&
         ccw(t.a, t.b, s.a) * ccw(t.a, t.b, s.b) <= 0;
}
}  // namespace geometry
namespace geometry {
bool is_intersect_sp(const Segment& s, const Point& p) {
  return ccw(s.a, s.b, p) == ON_SEGMENT;
}
}  // namespace geometry

namespace geometry {
Point cross_point_ll(const Line& l, const Line& m) {
  Real A = cross(l.b - l.a, m.b - m.a);
  Real B = cross(l.b - l.a, l.b - m.a);
  if (equals(abs(A), 0) && equals(abs(B), 0)) return m.a;
  return m.a + (m.b - m.a) * B / A;
}
}  // namespace geometry
namespace geometry {
Real distance_sp(const Segment& s, const Point& p) {
  Point r = projection(s, p);
  if (is_intersect_sp(s, r)) return abs(r - p);
  return min(abs(s.a - p), abs(s.b - p));
}
}  // namespace geometry
namespace geometry {
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_D
Real distance_ss(const Segment& a, const Segment& b) {
  if (is_intersect_ss(a, b)) return 0;
  return min({distance_sp(a, b.a), distance_sp(a, b.b), distance_sp(b, a.a),
              distance_sp(b, a.b)});
}
}  // namespace geometry
namespace geometry {
using Polygon = vector<Point>;
using Polygons = vector<Polygon>;
}  // namespace geometry

namespace geometry {
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_3_A
Real area(const Polygon& p) {
  int n = (int)p.size();
  Real A = 0;
  for (int i = 0; i < n; ++i) {
    A += cross(p[i], p[(i + 1) % n]);
  }
  return A * 0.5;
}
}  // namespace geometry
namespace geometry {
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_3_B
bool is_convex_polygon(const Polygon& p) {
  int n = (int)p.size();
  for (int i = 0; i < n; i++) {
    if (ccw(p[(i + n - 1) % n], p[i], p[(i + 1) % n]) == CLOCKWISE)
      return false;
  }
  return true;
}
}  // namespace geometry
namespace geometry {
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_3_C
int contains(const Polygon& Q, const Point& p) {
  bool in = false;
  for (int i = 0; i < Q.size(); i++) {
    Point a = Q[i] - p, b = Q[(i + 1) % Q.size()] - p;
    if (imag(a) > imag(b)) swap(a, b);
    if (sign(imag(a)) <= 0 && 0 < sign(imag(b)) && sign(cross(a, b)) < 0)
      in = !in;
    if (equals(cross(a, b), 0) && sign(dot(a, b)) <= 0) return ON;
  }
  return in ? IN : OUT;
}
}  // namespace geometry
namespace geometry {
int convex_polygon_contains(const Polygon& Q, const Point& p) {
  int N = (int)Q.size();
  Point g = (Q[0] + Q[N / 3] + Q[N * 2 / 3]) / 3.0;
  if (equals(imag(g), imag(p)) && equals(real(g), real(p))) return IN;
  Point gp = p - g;
  int l = 0, r = N;
  while (r - l > 1) {
    int mid = (l + r) / 2;
    Point gl = Q[l] - g;
    Point gm = Q[mid] - g;
    if (cross(gl, gm) > 0) {
      if (cross(gl, gp) >= 0 && cross(gm, gp) <= 0)
        r = mid;
      else
        l = mid;
    } else {
      if (cross(gl, gp) <= 0 && cross(gm, gp) >= 0)
        l = mid;
      else
        r = mid;
    }
  }
  r %= N;
  Real v = cross(Q[l] - p, Q[r] - p);
  return sign(v) == 0 ? ON : sign(v) == -1 ? OUT : IN;
}
}  // namespace geometry
namespace geometry {
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_4_A
Polygon convex_hull(Polygon& p, bool strict = true) {
  int n = (int)p.size(), k = 0;
  if (n <= 2) return p;
  sort(begin(p), end(p), compare_x);
  vector<Point> ch(2 * n);
  auto check = [&](int i) {
    return sign(cross(ch[k - 1] - ch[k - 2], p[i] - ch[k - 1])) <= -1 + strict;
  };
  for (int i = 0; i < n; ch[k++] = p[i++]) {
    while (k >= 2 && check(i)) --k;
  }
  for (int i = n - 2, t = k + 1; i >= 0; ch[k++] = p[i--]) {
    while (k >= t && check(i)) --k;
  }
  ch.resize(k - 1);
  return ch;
}
}  // namespace geometry
using namespace geometry;
int main(){
	ll n; cin >> n;
	Polygon a(n);
	rep(i, n)cin >> a[i];
  auto ans = convex_hull(a, false);
	cout << ans.size() << endl;
	ll idx = 0;
	rep(i, n){
		if(compare_y(ans[i], ans[idx])){
			idx = i;
		}
	}
	rep(i, ans.size())cout << fixed << setprecision(0)<< ans[(idx+i)%(int(ans.size()))] << endl;

	
}
accuracy: 1
time_ms: 110
memory_kb: 7780
final_rank: 13

code_snippet: #include<bits/stdc++.h>

using namespace std;

#define rep(i, l, n) for(int i = int(l); i < int(n); i++)
#define ll long long
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()

template<class T> bool chmin(T &a, T b) {if(a > b) {a = b; return true;} return false;}
template<class T> bool chmax(T &a, T b) {if(a < b) {a = b; return true;} return false;}
template<class T> using spq = priority_queue<T, vector<T>, greater<T>>;

// bool -> Yes/No
string answer(bool b) {return b ? "Yes" : "No";}

void fix(int k) {cout << fixed << setprecision(k);}

const int inf = 2e9;
const long long INF = 2e18;
const long double eps = 1e-12;
const long double pi = acos(-1);
int dx[] = {0, -1, 0, 1, -1, -1, 1, 1}, dy[] = {1, 0, -1, 0, 1, -1, -1, 1};

struct Point {

    long double x, y;

    Point() : x(0.00), y(0.00) {}
    Point(long double x_, long double y_) : x(x_), y(y_) {}

    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*=(const long double &k) {
        x *= k; y *= k;
        return (*this);
    }

    Point &operator*=(const Point &p) {
        long double x_ = x * p.x - y * p.y;
        long double y_ = x * p.y + y * p.x;
        x = x_; y = y_;
        return (*this);
    }
    
    Point &operator/=(const long double &k) {
        x /= k; y /= k;
        return (*this);
    }

    bool operator==(const Point &p) {
        return std::abs(x - p.x) < eps && std::abs(y - p.y) < eps;
    }

    bool operator!=(const Point &p) {
        return std::abs(x - p.x) >= eps || std::abs(y - p.y) >= eps;
    }

    Point operator+(const Point &p) {return Point(*this) += p;}
    Point operator-(const Point &p) {return Point(*this) -= p;}
    Point operator*(const long double &k) {return Point(*this) *= k;}
    Point operator*(const Point &p) {return Point(*this) *= p;}
    Point operator/(const long double &k) {return Point(*this) /= k;}
    
    Point operator+() {
        return (*this);
    }

    Point operator-() {
        return (*this) * -1;
    }

    // 原点からの距離
    long double abs() {
        return std::hypotl(x, y);
    }

    // 原点からの距離の２乗
    long double norm() {
        return x * x + y * y;
    }

    // 原点中心にthetaラジアン回転
    Point rot(const long double &theta) {
        long double u = cos(theta), v = sin(theta);
        return (*this) * Point(u, v);
    }

    // 内積
    long double dot(const Point &p) {
        return x * p.x + y * p.y;
    }

    // 外積
    long double det(const Point &p) {
        return x * p.y - y * p.x;
    }

    // x軸の正の向きからの角度
    long double arg() {
        return atan2(y, x);
    }

    friend std::istream& operator>>(std::istream &is, Point &p) {
        is >> p.x >> p.y;
        return is;
    }

    friend std::ostream& operator<<(std::ostream &os, Point &p) {
        os << '(' << p.x << "," << p.y << ')';
        return os;
    }

};

bool operator<(const Point &a, const Point &b) {
    return std::abs(a.y - b.y) >= eps ? a.y < b.y : a.x < b.x;
}

struct Line {
    
    Point a, b;

    Line() : a(), b() {}
    Line(Point a_, Point b_) : a(a_), b(b_) {}
    // 直線 sx + ty = c
    Line(long double s, long double t, long double c) {
        if(-eps < s < eps) a = Point(0, c / t), b = Point(1, c / t);
        else if(-eps < t < eps) a = Point(c / s, 0), b = Point(c / s, 1);
        else a = Point(0, c / t), b = Point(c / s, 0);
    }

    friend std::istream &operator>>(std::istream &is, Line &p) {
        is >> p.a >> p.b;
        return is;
    }

    friend std::ostream &operator<<(std::ostream &os, Line &p) {
        os << p.a << " -> " << p.b;
        return os;
    }

};

struct Segment : Line {
    
    Segment() : Line() {}
    Segment(Point a, Point b) : Line(a, b) {}

};

struct Circle {
    
    Point p;
    long double r;

    Circle() : p(), r(0.00) {}
    Circle(Point p_, long double r_) : p(p_), r(r_) {}

};

using Polygon = std::vector<Point>;

// a-b-cがなす角度のうち小さい方を返す
long double angle(Point a, Point b, Point c) {
    Point v = (a - b), u = (c - b);
    long double theta = std::abs(v.arg() - u.arg());
    return std::min<long double>(theta, 2.00 * pi - theta);
}

// 点の進行方向
// verify : https://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=10913642
int ccw(Point a, Point b, Point c) {
    long double k = (b - a).det(c - a);
    if(k > eps) return 1;
    else if(k < -eps) return -1;
    if((b - a).dot(c - a) < -eps) return -2;
    if((a - b).dot(c - b) < -eps) return 2;
    return 0;
}

// 直線の平行判定
// verify : https://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=10913644
bool parallel(Line a, Line b) {
    long double res = (a.b - a.a).det(b.b - b.a);
    return (-eps < res && res < eps);
}

// 直線の垂直判定
// verify : https://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=10913644
bool vertical(Line a, Line b) {
    long double res = (a.a - a.b).dot(b.a - b.b);
    return (-eps < res && res < eps);
}

// 点pから直線lに引いた垂線の足
// verify : https://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=10913645
Point projection(Line l, Point p) {
    long double t = (p - l.a).dot(l.a - l.b) / (l.a - l.b).norm();
    return l.a + (l.a - l.b) * t;
}

// 点pから線分lに引いた垂線の足
Point projection(Segment s, Point p) {
    long double t = (p - s.a).dot(s.a - s.b) / (s.a - s.b).norm();
    return s.a + (s.a - s.b) * t;
}

// 直線lについて点pと線対称な位置にある点
// verify : https://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=10913652
Point reflection(Line l, Point p) {
    return p + (projection(l, p) - p) * 2.00;
}

// 直線と点の交差判定
bool intersect(Line l, Point p) {
    return (std::abs(ccw(l.a, l.b, p)) != 1);
}

// 直線と直線の交差判定
bool intersect(Line l, Line m) {
    if(std::abs((l.b - l.a).det(m.b - m.a)) > eps) return true;
    return (std::abs((l.b - l.a).det(m.b - l.a)) < eps);
}

// 線分と点の交差判定
bool intersect(Segment s, Point p) {
    return (ccw(s.a, s.b, p) == 0);
}

// 線分と直線の交差判定
bool intersect(Segment s, Line l) {
    return ((l.b - l.a).det(s.a - l.a) * (l.b - l.a).det(s.b - l.a) < eps);
}

// 線分と線分の交差判定
// verify : https://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=10913656
bool intersect(Segment s, Segment t) {
    if(ccw(s.a, s.b, t.a) * ccw(s.a, s.b, t.b) > 0) return false;
    return (ccw(t.a, t.b, s.a) * ccw(t.a, t.b, s.b) <= 0);
}

// 円と点の交差判定
bool intersect(Circle c, Point p) {
    return (std::abs((p - c.p).abs() - c.r) < eps);
}

// 円と直線の交差判定
bool intersect(Circle c, Line l) {
    return ((c.p - projection(l, c.p)).abs() - c.r < eps);
}

// 円と線分の交差判定（交点の個数を返す）
int intersect(Circle c, Segment s) {
    if((projection(s, c.p) - c.p).norm() - c.r * c.r > eps) return 0;
    long double d1 = (c.p - s.a).abs(), d2 = (c.p - s.b).abs();
    if(c.r - d1 > eps && c.r - d2 > eps) return 0;
    if((c.r - d1 > eps && d2 - c.r > eps) || (d1 - c.r > eps && c.r - d2 > eps)) return 1;
    Point h = projection(s, c.p);
    // hが線分上にあるとき...２点で交差
    if((s.a - h).dot(s.b - h) < 0) return 2;
    return 0;
}

// 円と円の交差判定
// 外包(4), 外接(3), ２点で交わる(2), 内接(1), 内包(0)
// verify : https://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=10913658
int intersect(Circle c, Circle d) {
    if(c.r < d.r) std::swap(c, d);
    long double dist = (c.p - d.p).abs();
    if(dist - c.r - d.r > eps) return 4;
    if(std::abs(dist - c.r - d.r) < eps) return 3;
    if(dist - c.r + d.r > eps) return 2;
    if(abs(dist - c.r + d.r) < eps) return 1;
    return 0;
}

// 点と点の距離
long double distance(Point a, Point b) {
    return (a - b).abs();
}

// 直線と点の距離
long double distance(Line l, Point p) {
    return (p - projection(l, p)).abs();
}

// 直線と直線の距離
long double distance(Line l, Line m) {
    return intersect(l, m) ? 0.00 : distance(l, m.a);
}

// 線分と点の距離
long double distance(Segment s, Point p) {
    Point h = projection(s, p);
    if(intersect(s, h)) return (h - p).abs();
    return std::min<long double>((s.a - p).abs(), (s.b - p).abs());
}

// 線分と直線の距離
long double distance(Segment s, Line l) {
    if(intersect(s, l)) return 0.00;
    return std::min<long double>(distance(l, s.a), distance(l, s.b));
}

// 線分と線分の距離
// verify : https://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=10913660
long double distance(Segment s, Segment t) {
    if(intersect(s, t)) return 0.00;
    return std::min<long double>({distance(s, t.a), distance(s, t.b), distance(t, s.a), distance(t, s.b)});
}

// 直線と直線の交点（交点を持つ前提）
Point crosspoint(Line l, Line m) {
    long double s = (l.b - l.a).det(m.b - m.a);
    long double t = (l.b - l.a).det(l.b - m.a);
    if(std::abs(s) < eps && std::abs(t) < eps) return m.a;
    return m.a + (m.b - m.a) * t / s;
}

// 線分と線分の交点（交点を持つ前提）
// verify : https://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=10913666
Point crosspoint(Segment s, Segment t) {
    return crosspoint(Line(s), Line(t));
}

// 円と直線の交点（交点を持つ前提）
// 交点が一つの場合は、first == second == 交点
// verify : https://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=10913670
std::pair<Point, Point> crosspoint(Circle c, Line l) {
    Point h = projection(l, c.p), e = (l.b - l.a) / (l.b - l.a).abs();
    if(std::abs(distance(l, c.p) - c.r) < eps) return std::make_pair(h, h);
    long double d = std::sqrt(c.r * c.r - (h - c.p).norm());
    return std::make_pair(h - e * d, h + e * d);
}

// 円と線分の交点（交点を持つ前提）
// 交点が一つの場合は、first == second == 交点
std::pair<Point, Point> crosspoint(Circle c, Segment s) {
    Line l = Line(s.a, s.b);
    if(intersect(c, s) == 2) return crosspoint(c, l);
    std::pair<Point, Point> ret = crosspoint(c, l);
    if((s.a - ret.first).dot(s.b - ret.first) < 0) ret.second = ret.first;
    else ret.first = ret.second;
    return ret;
}

// 円と円の交点（交点を持つ前提）
// 交点が一つの場合は、first == second == 交点
// verify : https://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=10913672
std::pair<Point, Point> crosspoint(Circle c, Circle d) {
    long double dist = (c.p - d.p).abs();
    long double deg = std::acos((c.r * c.r + dist * dist - d.r * d.r) / (2.00 * c.r * dist));
    Point p = c.p + (d.p - c.p) * c.r / dist;
    Point p1 = c.p + (p - c.p).rot(deg);
    Point p2 = c.p + (p - c.p).rot(-deg);
    return std::make_pair(p1, p2);
}

// 点pを通る円の接線
// verify : https://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=10913675
std::pair<Line, Line> tangentline(Circle c, Point p) {
    std::pair<Point, Point> res = crosspoint(c, Circle(p, std::sqrt((c.p - p).norm() - c.r * c.r)));
    return std::make_pair(Line(res.first, p), Line(res.second, p));
}

// 円の共通接線
std::vector<Line> tangentline(Circle c, Circle d) {
    std::vector<Line> ret;
    if(c.r < d.r) std::swap(c, d);
    long double dist = (c.p - d.p).norm();
    if(std::abs(dist) < eps) return ret;
    Point u = (d.p - c.p) / std::sqrt(dist);
    Point v = u.rot(pi * 0.50);
    for(int s = -1; s <= 1; s += 2) {
        long double h = (c.r + s * d.r) / std::sqrt(dist);
        if(std::abs(1.00 - h * h) < eps) ret.emplace_back(c.p + u * c.r, c.p + (u + v) * c.r);
        else if(1.00 - h * h > eps) {
            Point u_ = u * h, v_ = v * std::sqrt(1 - h * h);
            ret.emplace_back(c.p + (u_ + v_) * c.r, d.p - (u_ + v_) * d.r * s);
            ret.emplace_back(c.p + (u_ - v_) * c.r, d.p - (u_ - v_) * d.r * s);
        }
    }
    return ret;
}

// 多角形の凸性判定
bool is_convex(Polygon p) {
    int n = int(p.size());
    for(int i = 0; i < n; i++) {
        if(ccw(p[(i + n - 1) % n], p[i], p[(i + 1) % n]) == -1) return false;
    }
    return true;
}

// 凸包（反時計回り）
Polygon convex_hull(Polygon p) {
    int n = int(p.size()), k = 0;
    if(n <= 2) return p;
    std::sort(p.begin(), p.end());
    std::vector<Point> ret;
    for(int i = 0; i < n; i++) {
        while(k > 1 && ccw(ret[k - 2], ret[k - 1], p[i]) < 0) {
            k--; ret.pop_back();
        }
        ret.emplace_back(p[i]); k++;
    }
    for(int i = n - 2, t = k; i >= 0; i--) {
        while(k > t && ccw(ret[k - 2], ret[k - 1], p[i]) < 0) {
            k--; ret.pop_back();
        }
        ret.emplace_back(p[i]); k++;
    }
    ret.pop_back(); k--;
    return ret;
}

void main_program() {
    int n; cin >> n;
    Polygon q;
    rep(i, 0, n) {
        Point p; cin >> p;
        q.emplace_back(p);
    }
    Polygon ans = convex_hull(q);
    cout << ans.size() << "\n";
    for(Point p : ans) cout << p.x << " " << p.y << "\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T = 1;
    //cin >> T;
    while(T--) {
        main_program();
    }
}
accuracy: 1
time_ms: 70
memory_kb: 17192
final_rank: 19

code_snippet: #include <bits/stdc++.h>
using namespace std;
#if __has_include(<atcoder/all>)
#include <atcoder/all>
using namespace atcoder;
#endif
#ifndef ONLINE_JUDGE
#define _GLIBCXX_DEBUG
#include <iostream>
template <typename T, typename U>
ostream& operator<<(ostream& os, const pair<T, U>& p) {return os<<'(' <<p.first<<", "<<p.second<<')';}
template <typename T>
ostream& operator<<(ostream& os, const vector<T>& v) {os<<'[';for(auto it=v.begin();it!=v.end();++it){os<<*it; if(next(it)!=v.end()){os<<", ";}}return os<<']';}
template <typename T>
ostream& operator<<(ostream& os, const vector<vector<T>>& vv) {os<<"[\n";for(auto it=vv.begin();it!=vv.end();++it){os<<"  "<<*it;if(next(it)!=vv.end()){os<<",\n";}else{os<<"\n";}}return os<<"]";}
#define dump(x) cerr << #x << " = " << (x) << '\n'
#else
#define dump(x) (void)0
#endif
using ll = long long;
using ld = long double;
using P = pair<ll,ll>;
template<class T> using pq = priority_queue<T>;
template<class T> using pq_g = priority_queue<T, vector<T>, greater<T>>;
#define out(x) cout<<x<<'\n'
#define all(v) (v).begin(),(v).end()
#define rall(v) (v).rbegin(),(v).rend()
#define OVERLOAD_REP(_1,_2,_3,name,...) name
#define REP1(i,n) for (auto i = std::decay_t<decltype(n)>{}; (i) < (n); ++(i))
#define REP2(i, l, r) for (auto i = (l); (i) < (r); ++(i))
#define rep(...) OVERLOAD_REP(__VA_ARGS__, REP2, REP1)(__VA_ARGS__)
template<class T> inline bool chmin(T& a, T b) {if(a > b){a = b; return true;} else {return false;}};
template<class T> inline bool chmax(T& a, T b) {if(a < b){a = b; return true;} else {return false;}};
const ll INF=(1LL<<60);
const ll mod=998244353;
using Graph = vector<vector<ll>>;
using Network = vector<vector<pair<ll,ll>>>;
using Grid = vector<string>;
const int dx[4] = {1, 0, -1, 0};
const int dy[4] = {0, 1, 0, -1};
const int dx2[8] = {1, 1,  1, 0,  0, -1, -1, -1};
const int dy2[8] = {1, 0, -1, 1, -1,  1,  0, -1};

// #include "../geo.hpp"

#pragma once

using D = long double;
const D EPS = 1e-12L;
const D PI = acosl(-1.0L);
using P2 = complex<D>;
struct L2{P2 a, b;};
struct S2{P2 a, b;};
using G2 = vector<P2>;

int sgn(D x){return (x>EPS)-(x<-EPS);}
int cmp(D a, D b){return sgn(a-b);}
bool eq(D a, D b){return cmp(a, b)==0;}
bool lt(D a, D b){return cmp(a, b)<0;}
bool le(D a, D b){return cmp(a, b)<=0;}
bool gt(D a, D b){return cmp(a, b)>0;}
bool ge(D a, D b){return cmp(a, b)>=0;}
bool eqP2(P2 a, P2 b){return eq(real(a), real(b)) && eq(imag(a), imag(b));}
bool ltP2(P2 a, P2 b) {
    return lt(real(a), real(b)) || (eq(real(a), real(b)) && lt(imag(a), imag(b)));
}

D dot(P2 a, P2 b) {return real(conj(a)*b);}
D cross(P2 a, P2 b) {return imag(conj(a)*b);}
int ccw(P2 a, P2 b, P2 c) {
    b-=a; c-=a;
    auto cr=cross(b, c), dt=dot(b, c);
    if (cr > EPS) return +1;       // counter clockwise
    if (cr < -EPS) return -1;      // clockwise
    if (dt < -EPS) return +2;         // c--a--b
    if (lt(norm(b), norm(c))) return -2; // a--b--c
    return 0;                  // a--c--b
}
int turn(P2 a, P2 b, P2 c) { return sgn(cross(b - a, c - a)); }

P2 rot(P2 a, D th) { return a*polar<D>(1, th); }
P2 rot90(P2 a) { return P2(-imag(a), real(a)); }
bool ltP2Arg(P2 a, P2 b) {
    auto up = [](P2 p) { return (imag(p) > 0 || (eq(imag(p), 0) && ge(real(p), 0))); };
    if (up(a) != up(b)) return up(a);
    if (!eq(cross(a, b), 0)) return cross(a, b) > 0;
    return lt(norm(a), norm(b));
}

inline void rd(P2& p) {D x, y; cin>>x>>y; p = P2(x, y);}
#define setp(n) cout<<setprecision(n)<<fixed
inline void pr(const P2& p) {setp(0); cout<<real(p)<<' '<<imag(p)<<'\n';}

// ----------------------------------------------------------------------------------

P2 proj(L2 l, P2 p) { P2 a=p-l.a, b=l.b-l.a; return l.a+b*dot(a, b)/norm(b);}
P2 refl(L2 l, P2 p) { return proj(l, p)*2.0L - p; }
bool isParallel(L2 l, L2 m){ return eq(cross(l.a-l.b, m.a-m.b), 0); }
bool isPerp(L2 l, L2 m){ return eq(dot(l.a-l.b, m.a-m.b), 0); }

bool isLL(L2 l, L2 m){ return !eq(cross(l.a-l.b, m.a-m.b), 0); }
bool isLS(L2 l, S2 s){ return turn(l.a, l.b, s.a)*turn(l.a, l.b, s.b)<=0; }
bool isSS(S2 s, S2 t) {
    return
    ccw(s.a,s.b,t.a)*ccw(s.a,s.b,t.b) <= 0 &&
    ccw(t.a,t.b,s.a)*ccw(t.a,t.b,s.b) <= 0;
}

P2 cpLL(L2 l, L2 m) {
    P2 a=l.b-l.a, b=m.b-m.a;
    D d=cross(a, b);
    if (eq(d, 0)) return P2(INF, INF); // parallel
    return l.a + a*cross(m.b-l.a,b)/d;
}

D distLP(L2 l, P2 p) { return abs(cross(l.b-l.a, p-l.a))/abs(l.b-l.a); }
D distSP(S2 s, P2 p) {
    P2 a=s.a, b=s.b;
    if (lt(dot(b-a, p-a), 0)) return abs(p-a);
    if (lt(dot(a-b, p-b), 0)) return abs(p-b);
    return distLP({a,b}, p);
}
D distSS(S2 s, S2 t) {
    if (isSS(s, t)) return 0;
    return min({distSP(s, t.a), distSP(s, t.b), distSP(t, s.a), distSP(t, s.b)});
}

D area(G2 g) {
    D s=0;
    int n=g.size();
    rep(i, n) s+=cross(g[i], g[(i+1)%n]);
    return s/2.0L;
}

bool isConvex(G2 g) {
    int n=g.size();
    if (n < 3) return false;
    int dir = 0;
    rep(i,n){
        int s = turn(g[i], g[(i+1)%n], g[(i+2)%n]);
        if (s == 0) return false; // -> continue : allows three points on a line
        if (dir == 0) dir = s;
        else if (dir*s < 0) return false;
    }
    return true;
}

int inG(G2 g, P2 p) {
    bool in=false;
    int n=g.size();
    rep(i, n) {
        P2 a=g[i], b=g[(i+1)%n];
        if (ccw(a, b, p) == 0) return 1; // on boundary
        if (a.imag() > b.imag()) swap(a, b);
        if (le(a.imag(), p.imag()) && lt(p.imag(), b.imag()) && turn(a, b, p) > 0) in=!in;
    }
    return in ? 2 : 0; // inside : outside
}

G2 hull(G2 g) {
    sort(all(g), ltP2);
    g.erase(unique(all(g), eqP2), g.end());
    if (g.size()<=1) return g;
    G2 lo, up;
    for (auto &p:g) {
        while (lo.size()>=2 && turn(lo[lo.size()-2], lo.back(), p)<0) lo.pop_back(); //-> turn()<0 : allow three points on a line
        lo.push_back(p);
    }
    reverse(all(g));
    for (auto &p:g) {
        while (up.size()>=2 && turn(up[up.size()-2], up.back(), p)<0) up.pop_back(); // -> turn()<0 : allow three points on a line
        up.push_back(p);
    }
    lo.pop_back(); up.pop_back();
    lo.insert(lo.end(), all(up));
    return lo;
}

pair<P2, P2> diameter(const G2& h) {
    int n=h.size();
    if(n==0) return {P2(0,0), P2(0,0)};
    if(n==1) return {h[0], h[0]};
    auto area2=[&](int i, int j, int k) {return abs(cross(h[j]-h[i], h[k]-h[i]));};
    D maxd=0;
    pair<P2, P2> res={h[0], h[0]};
    int j=1;
    rep(i, n) {
        int ni=(i+1)%n;
        while (gt(area2(i, ni, (j+1)%n), area2(i, ni, j))) j=(j+1)%n;
        if (gt(abs(h[i]-h[j]), maxd)) maxd=abs(h[i]-h[j]), res={h[i], h[j]};
        if (gt(abs(h[ni]-h[j]), maxd)) maxd=abs(h[ni]-h[j]), res={h[ni], h[j]};
    }
    return res;
}

// int inConvex(G2 h, P2 p) {
//     int n=h.size();
//     if (n==0) return 0;
//     if (n==1) return eqP2(h[0], p) ? 2 : 0;
//     if (n==2) return ccw(h[0], h[1], p)==0 && le(abs(h[0]-p)+abs(h[1]-p), abs(h[0]-h[1])) ? 2 : 0;
//     int left=1, right=n-1;
//     while (right-left>1) {
//         int mid=(left+right)/2;
//         if (turn(h[0], h[mid], p)<0) right=mid;
//         else left=mid;
//     }
//     if (turn(h[0], h[left], p)<0) return 0;
//     if (turn(h[left], h[right%n], p)<0) return 0;
//     if (turn(h[right%n], h[0], p)<0) return 0;
//     if (turn(h[0], h[left], p)==0 || turn(h[left], h[right%n], p)==0 || turn(h[right%n], h[0], p)==0) return 2;
//     return 1;
// }

G2 cutConvex(G2 h, L2 l) {
    int n=h.size();
    G2 res;
    if (n==0) return res;
    auto inside=[&](P2 p){ return turn(l.a, l.b, p) >= 0; };
    rep(i, n) {
        P2 a=h[i], b=h[(i+1)%n];
        bool ina=inside(a), inb=inside(b);
        if (ina&&inb) res.push_back(b);
        else if (ina&&!inb) {
            res.push_back(proj({a,b}, l.a));
        }
        else if (!ina&&inb) {
            res.push_back(proj({a,b}, l.a));
            res.push_back(b);
        }
    }
    return res;
}


pair<P2, P2> closestPair(G2 g) {
    auto ps = g;
    int n=ps.size();
    if (n < 2) return {P2(0,0), P2(0,0)};
    sort(all(ps), ltP2);
    D mind = norm(ps[0]-ps[1]);
    pair<P2, P2> res = {ps[0], ps[1]};
    set<pair<D,int>> box;
    int j=0;
    rep(i, n) {
        D xi=real(ps[i]), yi=imag(ps[i]);
        D rad = sqrtl(mind);
        while (j<i && gt(xi - real(ps[j]), rad)) {
            box.erase({imag(ps[j]), j});
            j++;
        }
        auto it = box.lower_bound({yi - rad, -1});
        for (; it != box.end() && le(it->first, yi + rad); ++it) {
            int k = it->second;
            D d = norm(ps[i]-ps[k]);
            if (lt(d, mind)) mind=d, res={ps[i], ps[k]}, rad = sqrtl(mind);
        }
        box.insert({yi, i});
    }
    return res;
}


struct C2 { P2 o; D r; };

C2 makeC2(P2 a, P2 b) {
    return C2{(a+b)/D(2), abs(a-b)/D(2)};
}
C2 makeC2(P2 a, P2 b, P2 c) {
    P2 B=b-a, C=c-a;
    D d=2*cross(B, C);
    if (eq(d, 0)) {
        D ab=norm(B), bc=norm(b-c), ca=norm(c-a);
        if (le(ab, bc) && le(ab, ca)) return makeC2(a, b);
        if (le(bc, ca)) return makeC2(b, c);
        return makeC2(c, a);
    }
    P2 o=a+(rot90(B)*norm(C)-rot90(C)*norm(B))/d;
    return C2{o, abs(o-a)};
}

C2 incircle(P2 a, P2 b, P2 c) {
    D ab=abs(b-a), bc=abs(c-b), ca=abs(a-c);
    P2 o=(a*bc+b*ca+c*ab)/(ab+bc+ca);
    D r=abs(cross(b-a, o-a))/abs(b-a);
    return C2{o, r};
}

vector<P2> cpCL(L2 l, C2 c) {
    vector<P2> res;
    P2 a=l.a-c.o, d=l.b-l.a;
    D A=norm(d), B=2*dot(a, d), C=norm(a)-c.r*c.r;
    D Dlt=B*B-4*A*C;
    if (lt(Dlt, 0)) return res;
    D s = le(Dlt, 0) ? 0 : sqrtl(max<D>(0, Dlt));
    D t1 = (-B - s)/(2*A), t2 = (-B + s)/(2*A);
    res.push_back(c.o + a + d*t1);
    if (gt(Dlt, 0)) res.push_back(c.o + a + d*t2);
    return res;
}

vector<P2> cpCC(C2 c1, C2 c2) {
    vector<P2> res;
    P2 d=c2.o-c1.o;
    D L=abs(d);
    if (eq(L, 0)) return res;
    D a=(c1.r*c1.r-c2.r*c2.r+L*L)/(2*L), h2=c1.r*c1.r-a*a;
    if (lt(h2, 0)) return res;
    P2 m=c1.o+d*(a/L), n=rot90(d)*( (lt(h2, 0) ? 0 : sqrtl(max<D>(0, h2))/L) );
    res.push_back(m-n);
    if (gt(h2, 0)) res.push_back(m+n);
    return res;
}

D isaCC(C2 c1, C2 c2) {
    D d=abs(c1.o-c2.o), r1=c1.r, r2=c2.r;
    if (ge(d, r1+r2)) return 0;
    if (le(d, abs(r1-r2))) {
        D r=min(r1, r2);
        return PI*r*r;
    }
    auto f = [&] (D z) { return max<D>(-1, min<D>(1, z)); };
    D a = 2*acosl(f((d*d+r1*r1-r2*r2)/(2*d*r1)));
    D b = 2*acosl(f((d*d+r2*r2-r1*r1)/(2*d*r2)));
    return 0.5L*(r1*r1*(a-sinl(a)) + r2*r2*(b-sinl(b)));
}

vector<L2> tangCP(C2 c, P2 p) {
    vector<L2> res;
    P2 v = p - c.o;
    D d2 = norm(v), r2 = c.r*c.r;
    if (lt(d2, r2)) return res;
    if (eq(d2, r2)) {
        res.push_back({c.o+v*(r2/d2), p});
        return res;
    }
    D h = c.r*sqrtl(max<D>(0, d2 - r2));
    P2 n1 = (v*r2 - rot90(v)*h)/d2;
    P2 n2 = (v*r2 + rot90(v)*h)/d2;
    res.push_back({c.o+n1, p});
    res.push_back({c.o+n2, p});
    return res;
}

vector<L2> tangCC(C2 c1, C2 c2) {
    vector<L2> res;
    P2 d = c2.o-c1.o;
    D L=abs(d);
    if (eq(L, 0)) return res;
    P2 e = d/L;
    auto add = [&] (int s) {
        D c=(s*c2.r - c1.r)/L;
        if (gt(c*c, 1)) return;
        D s2 = max<D>(0, 1 - c*c);
        if (eq(s2, 0)) {
            P2 n = e*c;
            res.push_back({c1.o-n*c1.r, c2.o-n*c2.r});
        } else {
            D s = sqrtl(s2);
            P2 n1 = e*c + rot90(e)*s;
            P2 n2 = e*c - rot90(e)*s;
            res.push_back({c1.o-n1*c1.r, c2.o-n1*c2.r});
            res.push_back({c1.o-n2*c1.r, c2.o-n2*c2.r});
        }
    };
    add(+1); // external
    add(-1); // internal
    return res;
}

C2 mec(G2 ps) {
    auto inC2=[&](C2 c, P2 p){ return le(abs(c.o - p), c.r); };
    mt19937_64 rng((uint64_t)chrono::steady_clock::now().time_since_epoch().count());
    shuffle(all(ps), rng);
    int n=ps.size();
    if(n==0) return C2{P2(0,0), 0};
    C2 c{ps[0], 0};
    rep(i, n) {
        if (inC2(c, ps[i])) continue;
        c={ps[i], 0};
        rep(j, i) {
            if (inC2(c, ps[j])) continue;
            c=makeC2(ps[i], ps[j]);
            rep(k, j) {
                if (inC2(c, ps[k])) continue;
                c=makeC2(ps[i], ps[j], ps[k]);
            }
        }
    }
    return c;
}

vector<P2> cpSegs(vector<S2> ss) {
    struct E { D x; int t; D y, y1, y2; }; // t: 0=add(H), 1=query(V), 2=remove(H)
    vector<E> es; es.reserve(ss.size()*2);

    for(auto s: ss) {
        if (eq(s.a.imag(), s.b.imag())) {
            if (gt(s.a.real(), s.b.real())) swap(s.a, s.b);
            es.push_back({s.a.real(), 0, s.a.imag(), 0, 0});
            es.push_back({s.b.real(), 2, s.a.imag(), 0, 0});
        } else {
            if (gt(s.a.imag(), s.b.imag())) swap(s.a, s.b);
            es.push_back({s.a.real(), 1, 0, s.a.imag(), s.b.imag()});
        }
    }

    sort(all(es), [](E a, E b) {
        if (!eq(a.x, b.x)) return lt(a.x, b.x);
        return a.t < b.t;
    });

    multiset<D> active;
    vector<P2> res; res.reserve(ss.size());
    for (auto e : es) {
        if (e.t == 0) { // add
            active.insert(e.y);
        } else if (e.t == 2) { // remove
            auto it = active.find(e.y);
            if (it != active.end()) active.erase(it);
        } else { // query
            for (auto it = active.lower_bound(e.y1); it != active.end() && le(*it, e.y2); ++it) {
                res.push_back(P2(e.x, *it));
            }
        }
    }

    sort(all(res), ltP2);
    res.erase(unique(all(res), eqP2), res.end());
    return res;
}


// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/1/CGL_1_A
void Projection() {
    P2 a, b; rd(a); rd(b);
    L2 l = {a, b};
    ll q;cin>>q;
    while(q--) {
        P2 p; rd(p);
        pr(proj(l, p));
    }
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/1/CGL_1_B
void Reflection() {
    P2 a, b; rd(a); rd(b);
    L2 l = {a, b};
    ll q;cin>>q;
    while(q--) {
        P2 p; rd(p);
        pr(refl(l, p));
    }
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/1/CGL_1_C
void CounterClockwise() {
    P2 p0, p1;
    rd(p0); rd(p1);
    ll q; cin>>q;
    while(q--) {
        P2 p; rd(p);
        int res = ccw(p0, p1, p);
        if (res == +1) out("COUNTER_CLOCKWISE");
        else if (res == -1) out("CLOCKWISE");
        else if (res == +2) out("ONLINE_BACK");
        else if (res == -2) out("ONLINE_FRONT");
        else out("ON_SEGMENT");
    }
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/1/CGL_2_A
void ParallelOrthogonal() {
    ll q;cin>>q;
    while(q--) {
        P2 p1, p2, p3, p4;
        rd(p1); rd(p2); rd(p3); rd(p4);
        if (isParallel({p1, p2}, {p3, p4})) out(2);
        else if(isPerp({p1, p2}, {p3, p4})) out(1);
        else out(0);
    }
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/2/CGL_2_B
void IntersectionSS() {
    ll q;cin>>q;
    while(q--) {
        P2 p1, p2, p3, p4;
        rd(p1); rd(p2); rd(p3); rd(p4);
        S2 s1 = {p1, p2}, s2 = {p3, p4};
        out(isSS(s1, s2));
    }
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/2/CGL_2_C
void CrossPointSS() {
    ll q;cin>>q;
    while(q--) {
        P2 p1, p2, p3, p4;
        rd(p1); rd(p2); rd(p3); rd(p4);
        L2 s1 = {p1, p2}, s2 = {p3, p4};
        pr(cpLL(s1, s2));
    }
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/2/CGL_2_D
void DistanceSS() {
    ll q;cin>>q;
    while(q--) {
        P2 p1, p2, p3, p4;
        rd(p1); rd(p2); rd(p3); rd(p4);
        S2 s1 = {p1, p2}, s2 = {p3, p4};
        setp(12);
        out(distSS(s1, s2));
    }
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/3/CGL_3_A
void Area() {
    ll n;cin>>n;
    G2 g(n);
    rep(i, n) rd(g[i]);
    setp(1);
    out(area(g));
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/3/CGL_3_B
void IsConvex() {
    ll n;cin>>n;
    G2 g(n);
    rep(i, n) rd(g[i]);
    out(isConvex(g));
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/3/CGL_3_C
void PolygonPointContainment() {
    ll n;cin>>n;
    G2 g(n);
    rep(i, n) rd(g[i]);
    ll q;cin>>q;
    while(q--) {
        P2 p; rd(p);
        out(inG(g, p));
    }
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/4/CGL_4_A
void ConvexHull() {
    ll n;cin>>n;
    G2 g(n);
    rep(i, n) rd(g[i]);
    G2 h = hull(g);
    out(h.size());
    ll sx = INF, sy = INF;
    ll id = -1;
    rep(i, h.size()) {
        if (lt(imag(h[i]), sy)) {
            sx = real(h[i]);
            sy = imag(h[i]);
            id = i;
        }
        else if (eq(imag(h[i]), sy) && lt(real(h[i]), sx)) {
            sx = real(h[i]);
            sy = imag(h[i]);
            id = i;
        }
    }
    rep(i, h.size()) {
        ll ni = (id + i) % h.size();
        pr(h[ni]);
    }
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/4/CGL_4_B
void DiameterOfConvexPolygon() {
    ll n;cin>>n;
    G2 g(n);
    rep(i, n) rd(g[i]);
    auto [p1, p2] = diameter(g);
    setp(12);
    out(abs(p1 - p2));
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/4/CGL_4_C
void ConvexCut() {
    ll n;cin>>n;
    G2 g(n);
    rep(i, n) rd(g[i]);
    ll q;cin>>q;
    while(q--) {
        P2 a, b; rd(a); rd(b);
        L2 l = {a, b};
        G2 h = cutConvex(g, l);
        setp(12);
        out(area(h));
    }
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/5/CGL_5_A
void ClosestPair() {
    ll n;cin>>n;
    G2 g(n);
    rep(i, n) rd(g[i]);
    auto [p1, p2] = closestPair(g);
    setp(12);
    out(abs(p1 - p2));
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/5/CGL_5_B
void MinimumEnclosingCircle() {
    ll n;cin>>n;
    G2 g(n);
    rep(i, n) rd(g[i]);
    auto [c, r] = mec(g);
    pr(c);
    setp(12);
    out(r);
}


// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/6/CGL_6_A
void SegmentIntersections() {
    ll n;cin>>n;
    vector<S2> seg(n);
    rep(i, n) {
        P2 a, b; rd(a); rd(b);
        seg[i] = {a, b};
    }
    out(cpSegs(seg).size());
}


// // https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/7/CGL_7_A
void IntersectionOfCircles() {
    P2 o1, o2; D r1, r2;
    rd(o1); cin>>r1;
    rd(o2); cin>>r2;
    C2 c1={o1, r1}, c2={o2, r2};
    out(tangCC(c1, c2).size());
}

// // https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/7/CGL_7_B
void IncircleOfTriangle() {
    P2 a, b, c; rd(a); rd(b); rd(c);
    auto [ic, ir] = incircle(a, b, c);
    pr(ic);
    setp(12);
    out(ir);
}

// // https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/7/CGL_7_C
void CircumscribedCircleOfTriangle() {
    P2 a, b, c; rd(a); rd(b); rd(c);
    auto [cc, cr] = makeC2(a, b, c);
    pr(cc);
    setp(12);
    out(cr);
}

// // https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/7/CGL_7_D
void CrossPointsCL() {
    P2 c; D r;
    rd(c); cin>>r;
    ll q;cin>>q;
    while(q--) {
        P2 a, b; rd(a); rd(b);
        L2 l = {a, b};
        auto ps = cpCL(l, {c, r});
        sort(all(ps), ltP2);
        pr(ps[0]);
        pr(ps[(ps.size()-1)]);
    }
}

// // https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/7/CGL_7_E
void CrossPointsCC() {
    P2 o1, o2; D r1, r2;
    rd(o1); cin>>r1;
    rd(o2); cin>>r2;
    C2 c1={o1, r1}, c2={o2, r2};
    auto ps = cpCC(c1, c2);
    sort(all(ps), ltP2);
    pr(ps[0]);
    pr(ps[(ps.size()-1)]);
}

// // https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/7/CGL_7_F
void TangentToCircle() {
    P2 p, c; D r;
    rd(p);
    rd(c); cin>>r;
    C2 c1={c, r};
    auto ls = tangCP(c1, p);
    vector<P2> ps;
    for (auto l : ls) {
        ps.push_back(l.a);
    }
    sort(all(ps), ltP2);
    for (auto p : ps) pr(p);
}

// // https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/7/CGL_7_G
void CommonTangent() {
    P2 o1, o2; D r1, r2;
    rd(o1); cin>>r1;
    rd(o2); cin>>r2;
    C2 c1={o1, r1}, c2={o2, r2};
    auto ls = tangCC(c1, c2);
    vector<P2> ps;
    for (auto l : ls) {
        ps.push_back(l.a);
    }
    sort(all(ps), ltP2);
    for (auto p : ps) pr(p);
}

// // https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/7/CGL_7_H
// void IntersectionCP() {
//     ll n;cin>>n;
//     D r; cin>>r;
//     C2 c = {P2(0, 0), r};
//     G2 g(n);
//     rep(i, n) rd(g[i]);
//     out(isaCP(c, g));
// }

// // https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/7/CGL_7_I
void AreaOfIntersectionBetweenTwoCircles() {
    P2 o1, o2; D r1, r2;
    rd(o1); cin>>r1;
    rd(o2); cin>>r2;
    C2 c1={o1, r1}, c2={o2, r2};
    setp(12);
    out(isaCC(c1, c2));
}

int main() {
    cin.tie(0);
    ios_base::sync_with_stdio(false);

    // Projection();
    // Reflection();
    // CounterClockwise();

    // ParallelOrthogonal();
    // IntersectionSS();
    // CrossPointSS();
    // DistanceSS();

    // Area();
    // IsConvex();
    // PolygonPointContainment();

    ConvexHull();
    // DiameterOfConvexPolygon();
    // ConvexCut();

    // ClosestPair();
    // MinimumEnclosingCircle();

    // SegmentIntersections();

    // IntersectionOfCircles();
    // IncircleOfTriangle();
    // CircumscribedCircleOfTriangle();
    // CrossPointsCL();
    // CrossPointsCC();
    // TangentToCircle();
    // CommonTangent();
    // IntersectionCP();
    // AreaOfIntersectionBetweenTwoCircles();
    return 0;
}
accuracy: 1
time_ms: 90
memory_kb: 13248
final_rank: 17

code_snippet: #include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <climits>
#include <unordered_map>
#include <queue>
#include <deque>
#include <math.h>
#include <bits/stdc++.h>
#define ll long long
#define Lt Line<T>
#define Pt Point<T>
using i64 = long long;
using namespace std;

const ll N=1e6+10,MOD=998244353;

typedef std::pair<ll,ll> pii;
typedef std::pair<ll,pii> piii;
//i64 n,m,k;

using ld = long double;
const ld PI = acos(-1);
const ld EPS = 1e-7;
const ld INF = numeric_limits<ld>::max();
#define cc(x) cout << fixed << setprecision(x);

ld fgcd(ld x, ld y) { // 实数域gcd
    return abs(y) < EPS ? abs(x) : fgcd(y, fmod(x, y));
}
template<class T, class S> bool equal(T x, S y) {
    return -EPS < x - y && x - y < EPS;
}
template<class T> int sign(T x) {
    if (-EPS < x && x < EPS) return 0;
    return x < 0 ? -1 : 1;
}

template<class T> struct Point { // 在C++17下使用 emplace_back 绑定可能会导致CE！
    T x, y;
    Point(T x_ = 0, T y_ = 0) : x(x_), y(y_) {} // 初始化
    template<class U> operator Point<U>() { // 自动类型匹配
        return Point<U>(U(x), U(y));
    }
    Point &operator+=(Point p) & { return x += p.x, y += p.y, *this; }
    Point &operator+=(T t) & { return x += t, y += t, *this; }
    Point &operator-=(Point p) & { return x -= p.x, y -= p.y, *this; }
    Point &operator-=(T t) & { return x -= t, y -= t, *this; }
    Point &operator*=(T t) & { return x *= t, y *= t, *this; }
    Point &operator/=(T t) & { return x /= t, y /= t, *this; }
    Point operator-() const { return Point(-x, -y); }
    friend Point operator+(Point a, Point b) { return a += b; }
    friend Point operator+(Point a, T b) { return a += b; }
    friend Point operator-(Point a, Point b) { return a -= b; }
    friend Point operator-(Point a, T b) { return a -= b; }
    friend Point operator*(Point a, T b) { return a *= b; }
    friend Point operator*(T a, Point b) { return b *= a; }
    friend Point operator/(Point a, T b) { return a /= b; }
    friend bool operator<(Point a, Point b) {
        return equal(a.x, b.x) ? a.y < b.y - EPS : a.x < b.x - EPS;
    }
    friend bool operator>(Point a, Point b) { return b < a; }
    friend bool operator==(Point a, Point b) { return !(a < b) && !(b < a); }
    friend bool operator!=(Point a, Point b) { return a < b || b < a; }
    friend auto &operator>>(istream &is, Point &p) {
        return is >> p.x >> p.y;
    }
    friend auto &operator<<(ostream &os, Point p) {
        return os << "(" << p.x << ", " << p.y << ")";
    }
};
template<class T> struct Line {
    Point<T> a, b;
    Line(Point<T> a_ = Point<T>(), Point<T> b_ = Point<T>()) : a(a_), b(b_) {}
    template<class U> operator Line<U>() { // 自动类型匹配
        return Line<U>(Point<U>(a), Point<U>(b));
    }
    friend auto &operator<<(ostream &os, Line l) {
        return os << "<" << l.a << ", " << l.b << ">";
    }
};

using Pd = Point<ld>;
using Ld = Line<ld>;


template<class T> T cross(Point<T> a, Point<T> b) { // 叉乘
    return a.x * b.y - a.y * b.x;
}
template<class T> T cross(Point<T> p1, Point<T> p2, Point<T> p0) { // 叉乘 (p1 - p0) x (p2 - p0);
    return cross(p1 - p0, p2 - p0);
}


template<class T> T dot(Point<T> a, Point<T> b) { // 点乘
    return a.x * b.x + a.y * b.y;
}
template<class T> T dot(Point<T> p1, Point<T> p2, Point<T> p0) { // 点乘 (p1 - p0) * (p2 - p0);
    return dot(p1 - p0, p2 - p0);
}

template <class T> ld dis(T x1, T y1, T x2, T y2) {
    ld val = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
    return sqrt(val);
}
template <class T> ld dis(Point<T> a, Point<T> b) {
    return dis(a.x, a.y, b.x, b.y);
}

template<class T> Point<T> rotate(Point<T> p1, Point<T> p2) { // 旋转
    Point<T> vec = p1 - p2;
    return {-vec.y, vec.x};
}

ld toDeg(ld x) { // 弧度转角度
    return x * 180 / PI;
}
ld toArc(ld x) { // 角度转弧度
    return PI / 180 * x;
}

Point<ld> rotate(Point<ld> p, ld rad) {
    return {p.x * cos(rad) - p.y * sin(rad), p.x * sin(rad) + p.y * cos(rad)};
}

Point<ld> rotate(Point<ld> a, Point<ld> b, ld rad) {
    ld x = (a.x - b.x) * cos(rad) + (a.y - b.y) * sin(rad) + b.x;
    ld y = (b.x - a.x) * sin(rad) + (a.y - b.y) * cos(rad) + b.y;
    return {x, y};
}

template<class T> bool onLine(Point<T> a, Point<T> b, Point<T> c) {
    return sign(cross(b, a, c)) == 0;
}
template<class T> bool onLine(Point<T> p, Line<T> l) {
    return onLine(p, l.a, l.b);
}

template<class T> bool pointOnLineLeft(Pt p, Lt l) {
    return cross(l.b, p, l.a) > 0;
}

template<class T> bool pointOnLineSide(Pt p1, Pt p2, Lt vec) {
    T val = cross(p1, vec.a, vec.b) * cross(p2, vec.a, vec.b);
    return sign(val) == 1;
}
template<class T> bool pointNotOnLineSide(Pt p1, Pt p2, Lt vec) {
    T val = cross(p1, vec.a, vec.b) * cross(p2, vec.a, vec.b);
    return sign(val) == -1;
}

Pd lineIntersection(Ld l1, Ld l2) {
    ld val = cross(l2.b - l2.a, l1.a - l2.a) / cross(l2.b - l2.a, l1.a - l1.b);
    return l1.a + (l1.b - l1.a) * val;
}

template<class T> bool lineParallel(Lt p1, Lt p2) {
    return sign(cross(p1.a - p1.b, p2.a - p2.b)) == 0;
}
template<class T> bool lineVertical(Lt p1, Lt p2) {
    return sign(dot(p1.a - p1.b, p2.a - p2.b)) == 0;
}
template<class T> bool same(Line<T> l1, Line<T> l2) {
    return lineParallel(Line{l1.a, l2.b}, {l1.b, l2.a}) &&
           lineParallel(Line{l1.a, l2.a}, {l1.b, l2.b}) && lineParallel(l1, l2);
}

pair<Pd, ld> pointToLine(Pd p, Ld l) {
    Pd ans = lineIntersection({p, p + rotate(l.a, l.b)}, l);
    return {ans, dis(p, ans)};
}

template<class T> ld disPointToLine(Pt p, Lt l) {
    ld ans = cross(p, l.a, l.b);
    return abs(ans) / dis(l.a, l.b); // 面积除以底边长
}

template<class T> bool pointOnSegment(Pt p, Lt l) { // 端点也算作在直线上
    return sign(cross(p, l.a, l.b)) == 0 && min(l.a.x, l.b.x) <= p.x && p.x <= max(l.a.x, l.b.x) &&
           min(l.a.y, l.b.y) <= p.y && p.y <= max(l.a.y, l.b.y);
}

pair<Pd, ld> pointToSegment(Pd p, Ld l) {
    if (sign(dot(p, l.b, l.a)) == -1) { // 特判到两端点的距离
        return {l.a, dis(p, l.a)};
    } else if (sign(dot(p, l.a, l.b)) == -1) {
        return {l.b, dis(p, l.b)};
    }
    return pointToLine(p, l);
}

template<class T> bool segmentIntersection(Lt l1, Lt l2) {
    auto [s1, e1] = l1;
    auto [s2, e2] = l2;
    auto A = max(s1.x, e1.x), AA = min(s1.x, e1.x);
    auto B = max(s1.y, e1.y), BB = min(s1.y, e1.y);
    auto C = max(s2.x, e2.x), CC = min(s2.x, e2.x);
    auto D = max(s2.y, e2.y), DD = min(s2.y, e2.y);
    return A >= CC && B >= DD && C >= AA && D >= BB &&
           sign(cross(s1, s2, e1) * cross(s1, e1, e2)) == 1 &&
           sign(cross(s2, s1, e2) * cross(s2, e2, e1)) == 1;
}

template<class T> int pointInPolygon(Point<T> a, vector<Point<T>> p) {
    int n = p.size();
    for (int i = 0; i < n; i++) {
        if (pointOnSegment(a, Line{p[i], p[(i + 1) % n]})) {
            return 2;
        }
    }
    int t = 0;
    for (int i = 0; i < n; i++) {
        auto u = p[i], v = p[(i + 1) % n];
        if (u.x < a.x && v.x >= a.x && pointOnLineLeft(a, Line{v, u})) {
            t ^= 1;
        }
        if (u.x >= a.x && v.x < a.x && pointOnLineLeft(a, Line{u, v})) {
            t ^= 1;
        }
    }
    return t == 1;
}

template<class T> bool cmp1(Point<T> &a,Point<T> &b)
{
    Point<T> a1(a.y,a.x);
    Point<T> b1(b.y,b.x);
    return a1<b1;
}

template<class T> vector<Point<T>> staticConvexHull(vector<Point<T>> A, int flag = 1) {
    int n = A.size();
    if (n <= 2) { // 特判
        return A;
    }
    vector<Point<T>> ans(n * 2);
    sort(A.begin(), A.end());
    int now = -1;
    
    for (int i = 0; i < n; i++) { // 维护下凸包
        while (now > 0 && cross(A[i], ans[now], ans[now - 1]) < 0) {
            now--;
        }
        ans[++now] = A[i];
    }
    int pre = now;
    for (int i = n - 2; i >= 0; i--) { // 维护上凸包
        while (now > pre && cross(A[i], ans[now], ans[now - 1]) < 0) {
            now--;
        }
        ans[++now] = A[i];
    }
    ans.resize(now);
    return ans;
}


int main() {
    
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n; cin>>n;
    vector<Point<ll>> P(n);
    for(auto &t:P) cin>>t;
    auto res=staticConvexHull(P);
    
    //sort(res.begin(),res.end(),cmp1<ll>);
    //res.erase(unique(res.begin(),res.end()),res.end());
    // cout<<res.size()<<'\n';

    // for(auto tee:res) cout<<tee.x<<' '<<tee.y<<'\n';

    vector<Point<ll>> res1; Point<ll> tem(INT_MAX,INT_MAX);
    int w;
    for(int i=0;i<res.size();i++) 
    {
        if(cmp1(res[i],tem))
        {
            tem=res[i];
            w=i;
        }
    }

    for(int i=w;i>=0;i--)
    {
        res1.push_back(res[i]);
    }
    for(int i=res.size()-1;i>w;i--) res1.push_back(res[i]);
    cout<<res1.size()<<'\n';
    for(auto re:res1) cout<<re.x<<' '<<re.y<<'\n';

    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 10384
final_rank: 6

code_snippet: #line 1 "a.cpp"
#define PROBLEM \
  "https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/4/CGL_4_A"
#line 2 "/workspaces/cp-container/libraries/template.hpp"
#include <bits/stdc++.h>
using namespace std;

#define rep(i, n) for (int i = 0; i < (int)(n); ++i)

using ll = long long;
using vi = vector<int>;
using vvi = vector<vi>;
using vll = vector<ll>;
using vvll = vector<vll>;

template <typename T>
bool chmax(T& a, const T& b) {
  if (a < b) {
    a = b;
    return true;
  }
  return false;
}

template <typename T>
bool chmin(T& a, const T& b) {
  if (a > b) {
    a = b;
    return true;
  }
  return false;
}
#line 3 "/workspaces/cp-container/libraries/geometry/core.hpp"

using Point = complex<double>;

struct Line {
  Point a, b;
  Line() = default;
  Line(const Point& _a, const Point& _b) : a(_a), b(_b) {}
};

constexpr double eps = 1.0e-9;

double dot(const Point& a, const Point& b) { return (conj(a) * b).real(); }
double cross(const Point& a, const Point& b) { return (conj(a) * b).imag(); }

Point projection(const Point& p0, const Point& p1, const Point& p) {
  Point base = p1 - p0, hyp = p - p0;
  double t = dot(base, hyp) / norm(base);
  return p0 + t * base;
}

Point reflection(const Point& p0, const Point& p1, const Point& p) {
  return 2.0 * projection(p0, p1, p) - p;
}

constexpr int COUNTER_CLOCKWISE = 1, CLOCKWISE = -1, ONLINE_BACK = 2,
              ONLINE_FRONT = -2, ON_SEGMENT = 0;

int ccw(const Point& p0, const Point& p1, const Point& p2) {
  Point a = p1 - p0, b = p2 - p0;
  if (eps < cross(a, b)) return COUNTER_CLOCKWISE;
  if (cross(a, b) < -eps) return CLOCKWISE;
  if (dot(a, b) < -eps) return ONLINE_BACK;
  if (norm(a) < norm(b)) return ONLINE_FRONT;
  return ON_SEGMENT;
}

bool is_parallel(const Line& l0, const Line& l1) {
  return abs(cross(l0.b - l0.a, l1.b - l1.a)) < eps;
}

bool is_orthogonal(const Line& l0, const Line& l1) {
  return abs(dot(l0.b - l0.a, l1.b - l1.a)) < eps;
}

bool has_intersection(Line s0, Line s1) {
  return ccw(s0.a, s0.b, s1.a) * ccw(s0.a, s0.b, s1.b) <= 0 &&
         ccw(s1.a, s1.b, s0.a) * ccw(s1.a, s1.b, s0.b) <= 0;
}

Point cross_point(const Line& l0, const Line& l1) {
  double d0 = cross(l0.b - l0.a, l1.b - l1.a),
         d1 = cross(l0.b - l0.a, l0.b - l1.a);
  if (abs(d0) < eps && abs(d1) < eps) return l1.a;
  return l1.a + d1 / d0 * (l1.b - l1.a);
}

double distance_between_point_and_segment(const Point& p, const Line& s) {
  if (dot(s.b - s.a, p - s.a) < eps) return abs(p - s.a);
  if (dot(s.a - s.b, p - s.b) < eps) return abs(p - s.b);
  return abs(cross(s.b - s.a, p - s.a)) / abs(s.b - s.a);
}

double distance_between_segments(const Line& s0, const Line& s1) {
  if (has_intersection(s0, s1)) return 0.0;
  double ret = numeric_limits<double>::max();
  chmin(ret, distance_between_point_and_segment(s0.a, s1));
  chmin(ret, distance_between_point_and_segment(s0.b, s1));
  chmin(ret, distance_between_point_and_segment(s1.a, s0));
  chmin(ret, distance_between_point_and_segment(s1.b, s0));
  return ret;
}

using Polygon = vector<Point>;

double area(const Polygon& g) {
  int n = g.size();
  double ret = 0.0;
  rep(i, n) ret += cross(g[i], g[(i + 1) % n]);
  ret /= 2.0;
  return ret;
}

bool is_convex(const Polygon& g) {
  int n = g.size();
  rep(i, n) {
    if (ccw(g[(i - 1 + n) % n], g[i], g[(i + 1) % n]) == CLOCKWISE)
      return false;
  }
  return true;
}

int polygon_point_containment(const Polygon& g, const Point& p) {
  bool is_contained = false;
  int n = g.size();
  rep(i, n) {
    Point a = g[i] - p, b = g[(i + 1) % n] - p;
    if (a.imag() > b.imag()) swap(a, b);
    if (a.imag() < eps && eps < b.imag() && cross(a, b) < -eps)
      is_contained = !is_contained;
    if (abs(cross(a, b)) < eps && dot(a, b) < eps) return 1;
  }
  return is_contained ? 2 : 0;
}

vi convex_hull(const Polygon& g) {
  int n = g.size();
  if (n == 0) return {};
  vi p_idx(n);
  iota(p_idx.begin(), p_idx.end(), 0);
  ranges::sort(p_idx, [&](int a, int b) {
    if (eps < abs(g[a].real() - g[b].real())) return g[a].real() < g[b].real();
    return g[a].imag() < g[b].imag() - eps;
  });
  vvi hull(2);
  rep(i, 2) {
    for (int idx : p_idx) {
      while (2 <= (int)hull[i].size() &&
             ccw(g[hull[i][(int)hull[i].size() - 2]], g[hull[i].back()],
                 g[idx]) == CLOCKWISE) {
        hull[i].pop_back();
      }
      hull[i].emplace_back(idx);
    }
    hull[i].pop_back();
    ranges::reverse(p_idx);
  }
  ranges::copy(hull[1], back_inserter(hull[0]));
  return hull[0];
}
#line 5 "a.cpp"
signed main() {
  cin.tie(nullptr)->sync_with_stdio(false);
  int n;
  cin >> n;
  vi x(n), y(n);
  Polygon g(n);
  rep(i, n) {
    cin >> x[i] >> y[i];
    g[i] = Point(x[i], y[i]);
  }
  vi res = convex_hull(g);
  pair<int, int> mn = {1 << 30, 1 << 30};
  int mni = -1, siz = res.size();
  rep(i, siz) {
    if (chmin(mn, make_pair(y[res[i]], x[res[i]]))) mni = i;
  }
  cout << siz << endl;
  rep(i, siz) cout << x[res[(mni + i) % siz]] << ' ' << y[res[(mni + i) % siz]]
                   << '\n';
}
accuracy: 1
time_ms: 10
memory_kb: 6432
final_rank: 2

code_snippet: #include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'

#define maxn 100010


struct Point{
    double x,y;
    bool operator <(Point p){
        if(x!=p.x)return x<p.x;
        return y<p.y;
    }
};

double det(Point a1,Point a2,Point b1,Point b2){
    return (a2.x-a1.x)*(b2.y-b1.y)-(b2.x-b1.x)*(a2.y-a1.y);
}
double dis(Point a,Point b){
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

double tot;
int n;
Point point[maxn];
int s[maxn],top;
vector<int>f;
signed main(){
    cin>>n;
    for(int i =1;i<=n;i++)cin>>point[i].x>>point[i].y;
    sort(point+1,point+1+n);
    
    s[++top]=1;
    for(int i = 2;i<=n;i++){
        while(top>1&&det(point[s[top-1]],point[s[top]],point[s[top]],point[i])<0){
            s[top--]=0;
        }
        s[++top]=i;
    }
    for(int i =1;i<top;i++){
        tot+=dis(point[s[i]],point[s[i+1]]);
        f.push_back(s[i]);
    }
    
    top=0;
    s[++top]=n;
    for(int i = n-1;i>=1;i--){
        while(top>1&&det(point[s[top-1]],point[s[top]],point[s[top]],point[i])<0){
            s[top--]=0;
        }
        s[++top]=i;
    }
    for(int i =1;i<top;i++){
        tot+=dis(point[s[i]],point[s[i+1]]);
        f.push_back(s[i]);
    }
   // printf("%.2lf\n",tot);
    cout<<f.size()<<endl;
    int pos =0;
    double miy = 1e18;
    for(int it = 0;it<f.size();it++){
        if(miy>point[f[it]].y){
            miy = point[f[it]].y;
            pos = it;
        }
    }
   // cout<<pos<<endl;
    //for(auto it:f)cout<<point[it].x<<' '<<point[it].y<<endl;
    for(int i = pos;i<f.size();i++){
        //cout<<i<<endl;
        cout<<point[f[i]].x<<' '<<point[f[i]].y<<endl;
    }
     for(int i = 0;i<pos;i++){
        cout<<point[f[i]].x<<' '<<point[f[i]].y<<endl;
    }

}
accuracy: 1
time_ms: 80
memory_kb: 5788
final_rank: 9

code_snippet: #include<bits/stdc++.h>
#define siz(x) int((x).size())
#define all(x) std::begin(x),std::end(x)
#define fi first
#define se second
using namespace std;
using unt=unsigned;
using loli=long long;
using lolu=unsigned long long;
using pii=pair<int,int>;
mt19937_64 rng(random_device{}());
constexpr double eps=1e-8;
template<typename any>inline any sqr(const any &x){return x*x;}
inline int get_op(double x){if(fabs(x)<eps)return 0;return x>0?1:-1;}
double det(array<double,3>a0,array<double,3>a1,array<double,3>a2){
	double sum=0;
	sum+=a0[0]*a1[1]*a2[2];
	sum+=a1[0]*a2[1]*a0[2];
	sum+=a2[0]*a0[1]*a1[2];
	sum-=a0[2]*a1[1]*a2[0];
	sum-=a0[1]*a1[0]*a2[2];
	sum-=a0[0]*a1[2]*a2[1];
	return sum;
}
struct bector{
	double x,y;
	bool operator<(const bector&t)const{return fabs(x-t.x)<eps?y<t.y:x<t.x;}
	bool operator==(const bector&t)const{return fabs(x-t.x)<eps&&fabs(y-t.y)<eps;}
	bector(double a=0,double b=0):x(a),y(b){}
	double operator~()const{return sqrt(x*x+y*y);}
	double operator*()const{return x*x+y*y;}
	friend istream&operator>>(istream&a,bector&b){return a>>b.x>>b.y;}
	friend ostream&operator<<(ostream&a,const bector&b){return a<<b.x<<' '<<b.y;}
	friend bector operator+(const bector&a,const bector&b){return {a.x+b.x,a.y+b.y};}
	bector&operator+=(const bector&t){x+=t.x,y+=t.y;return *this;}
	friend bector operator-(const bector&a,const bector&b){return {a.x-b.x,a.y-b.y};}
	bector&operator-=(const bector&t){x-=t.x,y-=t.y;return *this;}
	friend bector operator*(const bector&a,double b){return {a.x*b,a.y*b};}
	friend bector operator*(double b,const bector&a){return {a.x*b,a.y*b};}
	bector&operator*=(double t){x*=t,y*=t;return *this;}
	friend bector operator/(const bector&a,double b){return {a.x/b,a.y/b};}
	bector&operator/=(double t){x/=t,y/=t;return *this;}
	friend double operator*(const bector&a,const bector&b){return a.x*b.x+a.y*b.y;}
	friend double operator/(const bector&a,const bector&b){return a.x*b.y-a.y*b.x;}
	bool frontis(const bector&t)const{auto r=*this*t;return get_op(r)>0;}
	bool backis(const bector&t)const{auto r=*this*t;return get_op(r)<0;}
	bool leftis(const bector&t)const{auto r=*this/t;return get_op(r)>0;}
	bool rightis(const bector&t)const{auto r=*this/t;return get_op(r)<0;}
	friend double dis(const bector&a,const bector&b){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}
	bector rot90()const{return {-y,x};}
	double arg()const{return atan2(y,x);}
	bector rotate(const bector&a,double r){
		auto u=a-*this;
		return *this+bector{u.x*cos(r)-u.y*sin(r),u.x*sin(r)+u.y*cos(r)};
	}
	friend double c0s(const bector&x,const bector&y){
		// 求 cos<x,y>
		return x*y/~x/~y;
	}
	bector unit()const{
		return (*this)/~(*this);
	}
	friend bool operator&(const bector&a,const bector&b){return !get_op(a*b);}
	friend bool operator|(const bector&a,const bector&b){return !get_op(a/b);}
};
bector polar(double p,double r){return {cos(r)*p,sin(r)*p};}
struct segment{
	bector p1,p2;
	segment(const bector&a={0,0},const bector&b={0,0}):p1(a),p2(b){};
	friend istream&operator>>(istream&a,segment &b){return a>>b.p1>>b.p2;}
	friend ostream&operator<<(ostream&a,const segment&b){return a<<b.p1<<' '<<b.p2;}
	friend bool operator&(const segment&a,const segment&b){return !get_op((a.p2-a.p1)*(b.p2-b.p1));}
	friend bool operator|(const segment&a,const segment&b){return !get_op((a.p2-a.p1)/(b.p2-b.p1));}
	friend bool banana(const segment&a,const segment&b){
		// 判断是否相交
		if(min(a.p1.x,a.p2.x)>max(b.p1.x,b.p2.x)||min(b.p1.x,b.p2.x)>max(a.p1.x,a.p2.x)||min(a.p1.y,a.p2.y)>max(b.p1.y,b.p2.y)||min(b.p1.y,b.p2.y)>max(a.p1.y,a.p2.y))return false;
		return get_op(((a.p2-a.p1)/(b.p1-a.p1))*((a.p2-a.p1)/(b.p2-a.p1)))<=0&&get_op(((b.p2-b.p1)/(a.p1-b.p1))*((b.p2-b.p1)/(a.p2-b.p1)))<=0;
	}
	friend bector focus(const segment&a,const segment&b){
		// 输入必须保证有交，输出交点
		bector v=a.p2-a.p1,w=b.p2-b.p1,u=a.p1-b.p1;
		return a.p1+v*((w/u)/(v/w));
	}
	friend double dis(const segment&a,const bector&b){
		if(a.p1==a.p2)return ~(b-a.p1);
		auto v1=a.p2-a.p1,v2=b-a.p1,v3=b-a.p2;
		if(v1.backis(v2))return ~v2;
		if(v1.frontis(v3))return ~v3;
		return fabs(v1/v2)/~v1;
	}
	friend double dis(const segment&a,const segment&b){
		// 求线段间最短距离
		if(banana(a,b))return 0;
		return min({dis(a,b.p1),dis(a,b.p2),dis(b,a.p1),dis(b,a.p2)});
	}
	bector project(const bector&a)const{
		// 求点 a 在当前线段上的投影点坐标
		if(a==p1||a==p2)return a;
		auto u=p2-p1,v=a-p1;
		return c0s(u,v)*(~v)*u.unit()+p1;
	}
	bector reflect(const bector&a)const{
		// 求点 a 在当前线段上的对称点
		return 2*project(a)-a;
	}
	segment&sort(){if(p2<p1)std::swap(p1,p2);return *this;}
};
struct circle{
	bector p;double r;
	circle(const bector&a={0,0},double b=0):p(a),r(b){}
	circle(bector p1,bector p2){
		p=(p1+p2)/2;
		r=dis(p1,p2)/2;
	}
	circle(bector p1,bector p2,bector p3){
		p.x=det({*p1,p1.y,1},{*p2,p2.y,1},{*p3,p3.y,1})/2/det({p1.x,p1.y,1},{p2.x,p2.y,1},{p3.x,p3.y,1});
		p.y=-det({*p1,p1.x,1},{*p2,p2.x,1},{*p3,p3.x,1})/2/det({p1.x,p1.y,1},{p2.x,p2.y,1},{p3.x,p3.y,1});
		r=dis(p,p1);
	}
	friend istream&operator>>(istream&a,circle &b){return a>>b.p>>b.r;}
	friend ostream&operator<<(ostream&a,const circle &b){return a<<b.p<<' '<<b.r;}
	bool check(bector t){
		return dis(p,t)-r<=eps;
	}
	friend segment focus(const circle &a,const segment&b){
		auto d=b.project(a.p),u=b.p2-b.p1,e=u/~u;
		auto t=sqrt(a.r*a.r-sqr(dis(d,a.p)));
		return segment{d+e*t,d-e*t}.sort();
	}
	friend segment focus(const circle &a,const circle &b){
		auto d=dis(a.p,b.p),l=acos((sqr(a.r)+sqr(d)-sqr(b.r))/(2*a.r*d)),t=(b.p-a.p).arg();
		return segment{a.p+polar(a.r,t+l),a.p+polar(a.r,t-l)}.sort();
	}
	segment tangent(const bector&a){
		auto u=a-p;
		auto l=acos(r/~u);
		u=u*r/~u;
		return segment{p.rotate(p+u,+l),p.rotate(p+u,-l)}.sort();
	}
};
struct points:vector<bector>{
#define p (*this)
	points&read(istream&t,int n=-1){
		if(n==-1)cin>>n;
		p.resize(n);
		for(int i=0;i<n;i++)t>>p[i];
		return *this;
	}
	double area()const{
		// 无需满足多边形是凸的
		double ans=0;
		for(int i=1;i<siz(p)-1;i++)ans+=(p[i]-p[0])/(p[i+1]-p[0]);
		return ans/2;
	}
	points tobag(){
		sort(all(p));
		points b1,b2;
		b1.push_back(p[0]),b1.push_back(p[1]);
		for(int i=2;i<siz(p);i++){
			for(int j=siz(b1)-1;j>=1&&(b1[j]-b1[j-1]).leftis(p[i]-b1[j-1]);j--)
				b1.pop_back();
			b1.push_back(p[i]);
		}
		b2.push_back(p.back()),b2.push_back(p[siz(p)-2]);
		for(int i=siz(p)-3;~i;i--){
			for(int j=siz(b2)-1;j>=1&&(b2[j]-b2[j-1]).leftis(p[i]-b2[j-1]);j--)
				b2.pop_back();
			b2.push_back(p[i]);
		}
		reverse(all(b2));
		for(int i=siz(b1)-2;i;i--)
			b2.push_back(b1[i]);
		if(siz(p)<=3)for(int i=0;i<siz(b2)-1;i++)
			if(b2[i]==b2.back()){
				b2.pop_back();
				break;
			}
		return b2;
	}
	double xzqk(){
		double ans=0;
		for(size_t i=0,j=1;i<p.size();i++){
			while((p[(i+1)%p.size()]-p[i])/(p[j]-p[i])<(p[(i+1)%p.size()]-p[i])/(p[(j+1)%p.size()]-p[i]))(++j)%=p.size();
			ans=max({ans,dis(p[i],p[j]),dis(p[(i+1)%p.size()],p[j])});
		}
		return ans;
	}
	circle incircle(){
		assert(p.size()==3);
		auto u=p[1]-p[0],v=p[2]-p[0];u=u/~u*~v;
		segment l1{p[0],p[0]+u+v};
		u=p[2]-p[1],v=p[0]-p[1];u=u/~u*~v;
		segment l2{p[1],p[1]+u+v};
		auto pp=focus(l1,l2);
		return circle{pp,dis(segment{p[0],p[1]},pp)};
	}
	circle outcirce(){
		assert(p.size()==3);
		auto u=p[1]-p[0],mid=(p[0]+p[1])/2;
		segment l1{mid,mid+u.rot90()};
		u=p[2]-p[0],mid=(p[0]+p[2])/2;
		segment l2{mid,mid+u.rot90()};
		auto pp=focus(l1,l2);
		return circle{pp,dis(p[0],pp)};
	}
	int contains(bector t){
		// 无需满足多边形是凸的
		// 2表示包含，1表示在边上，0表示在外面
		bool res=false;
		for(int i=0;i<siz(p);i++){
			auto a=p[i]-t,b=p[(i+1)%siz(p)]-t;
			if(!a.leftis(b)&&!a.rightis(b)&&!a.frontis(b))return 1;
			if(a.y>b.y)std::swap(a,b);
			if(a.y<eps&&eps<b.y&&a.leftis(b))res=!res;
		}
		return res?2:0;
	}
#undef p
};
circle mingenshin(points b){
	shuffle(all(b),rng);
	circle ans;
	for(int i=0;i<siz(b);i++)if(!ans.check(b[i])){
		ans={b[i],0};
		for(int j=0;j<i;j++)if(!ans.check(b[j])){
			ans=circle(b[i],b[j]);
			for(int k=0;k<j;k++)if(!ans.check(b[k])){
				ans=circle(b[i],b[j],b[k]);
			}
		}
	}
	return ans;
}
points a;
signed main(){
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	std::ios::sync_with_stdio(false);cin.tie(nullptr);
	cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(0);
	auto b=a.read(cin).tobag();
	cout<<siz(b)<<'\n';
	for(int i=0;;i++)if(b[i%siz(b)].y<=b[(i+1)%siz(b)].y){
		for(int j=i%siz(b);j<siz(b);j++)cout<<b[j]<<'\n';
		for(int j=0;j<i%siz(b);j++)cout<<b[j]<<'\n';
		break;
	}
	return 0;
}
accuracy: 1
time_ms: 70
memory_kb: 7076
final_rank: 12

code_snippet: #include<bits/stdc++.h>
#define siz(x) int((x).size())
#define all(x) std::begin(x),std::end(x)
#define fi first
#define se second
using namespace std;
using unt=unsigned;
using loli=long long;
using lolu=unsigned long long;
using pii=pair<int,int>;
mt19937_64 rng(random_device{}());
constexpr double eps=1e-8;
template<typename any>inline any sqr(const any &x){return x*x;}
inline int get_op(double x){if(fabs(x)<eps)return 0;return x>0?1:-1;}
double det(array<double,3>a0,array<double,3>a1,array<double,3>a2){
	double sum=0;
	sum+=a0[0]*a1[1]*a2[2];
	sum+=a1[0]*a2[1]*a0[2];
	sum+=a2[0]*a0[1]*a1[2];
	sum-=a0[2]*a1[1]*a2[0];
	sum-=a0[1]*a1[0]*a2[2];
	sum-=a0[0]*a1[2]*a2[1];
	return sum;
}
struct bector{
	double x,y;
	bool operator<(const bector&t)const{return fabs(x-t.x)<eps?y<t.y:x<t.x;}
	bool operator==(const bector&t)const{return fabs(x-t.x)<eps&&fabs(y-t.y)<eps;}
	bector(double a=0,double b=0):x(a),y(b){}
	double operator~()const{return sqrt(x*x+y*y);}
	double operator*()const{return x*x+y*y;}
	friend istream&operator>>(istream&a,bector&b){return a>>b.x>>b.y;}
	friend ostream&operator<<(ostream&a,const bector&b){return a<<b.x<<' '<<b.y;}
	friend bector operator+(const bector&a,const bector&b){return {a.x+b.x,a.y+b.y};}
	bector&operator+=(const bector&t){x+=t.x,y+=t.y;return *this;}
	friend bector operator-(const bector&a,const bector&b){return {a.x-b.x,a.y-b.y};}
	bector&operator-=(const bector&t){x-=t.x,y-=t.y;return *this;}
	friend bector operator*(const bector&a,double b){return {a.x*b,a.y*b};}
	friend bector operator*(double b,const bector&a){return {a.x*b,a.y*b};}
	bector&operator*=(double t){x*=t,y*=t;return *this;}
	friend bector operator/(const bector&a,double b){return {a.x/b,a.y/b};}
	bector&operator/=(double t){x/=t,y/=t;return *this;}
	friend double operator*(const bector&a,const bector&b){return a.x*b.x+a.y*b.y;}
	friend double operator/(const bector&a,const bector&b){return a.x*b.y-a.y*b.x;}
	bool frontis(const bector&t)const{auto r=*this*t;return get_op(r)>0;}
	bool backis(const bector&t)const{auto r=*this*t;return get_op(r)<0;}
	bool leftis(const bector&t)const{auto r=*this/t;return get_op(r)>0;}
	bool rightis(const bector&t)const{auto r=*this/t;return get_op(r)<0;}
	friend double dis(const bector&a,const bector&b){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}
	bector rot90()const{return {-y,x};}
	double arg()const{return atan2(y,x);}
	bector rotate(const bector&a,double r){
		auto u=a-*this;
		return *this+bector{u.x*cos(r)-u.y*sin(r),u.x*sin(r)+u.y*cos(r)};
	}
	friend double c0s(const bector&x,const bector&y){
		// 求 cos<x,y>
		return x*y/~x/~y;
	}
	bector unit()const{
		return (*this)/~(*this);
	}
	friend bool operator&(const bector&a,const bector&b){return !get_op(a*b);}
	friend bool operator|(const bector&a,const bector&b){return !get_op(a/b);}
};
bector polar(double p,double r){return {cos(r)*p,sin(r)*p};}
struct segment{
	bector p1,p2;
	segment(const bector&a={0,0},const bector&b={0,0}):p1(a),p2(b){};
	friend istream&operator>>(istream&a,segment &b){return a>>b.p1>>b.p2;}
	friend ostream&operator<<(ostream&a,const segment&b){return a<<b.p1<<' '<<b.p2;}
	friend bool operator&(const segment&a,const segment&b){return !get_op((a.p2-a.p1)*(b.p2-b.p1));}
	friend bool operator|(const segment&a,const segment&b){return !get_op((a.p2-a.p1)/(b.p2-b.p1));}
	friend bool banana(const segment&a,const segment&b){
		// 判断是否相交
		if(min(a.p1.x,a.p2.x)>max(b.p1.x,b.p2.x)||min(b.p1.x,b.p2.x)>max(a.p1.x,a.p2.x)||min(a.p1.y,a.p2.y)>max(b.p1.y,b.p2.y)||min(b.p1.y,b.p2.y)>max(a.p1.y,a.p2.y))return false;
		return get_op(((a.p2-a.p1)/(b.p1-a.p1))*((a.p2-a.p1)/(b.p2-a.p1)))<=0&&get_op(((b.p2-b.p1)/(a.p1-b.p1))*((b.p2-b.p1)/(a.p2-b.p1)))<=0;
	}
	friend bector focus(const segment&a,const segment&b){
		// 输入必须保证有交，输出交点
		bector v=a.p2-a.p1,w=b.p2-b.p1,u=a.p1-b.p1;
		return a.p1+v*((w/u)/(v/w));
	}
	friend double dis(const segment&a,const bector&b){
		if(a.p1==a.p2)return ~(b-a.p1);
		auto v1=a.p2-a.p1,v2=b-a.p1,v3=b-a.p2;
		if(v1.backis(v2))return ~v2;
		if(v1.frontis(v3))return ~v3;
		return fabs(v1/v2)/~v1;
	}
	friend double dis(const segment&a,const segment&b){
		// 求线段间最短距离
		if(banana(a,b))return 0;
		return min({dis(a,b.p1),dis(a,b.p2),dis(b,a.p1),dis(b,a.p2)});
	}
	bector project(const bector&a)const{
		// 求点 a 在当前线段上的投影点坐标
		if(a==p1||a==p2)return a;
		auto u=p2-p1,v=a-p1;
		return c0s(u,v)*(~v)*u.unit()+p1;
	}
	bector reflect(const bector&a)const{
		// 求点 a 在当前线段上的对称点
		return 2*project(a)-a;
	}
	segment&sort(){if(p2<p1)std::swap(p1,p2);return *this;}
};
struct circle{
	bector p;double r;
	circle(const bector&a={0,0},double b=0):p(a),r(b){}
	circle(bector p1,bector p2){
		p=(p1+p2)/2;
		r=dis(p1,p2)/2;
	}
	circle(bector p1,bector p2,bector p3){
		p.x=det({*p1,p1.y,1},{*p2,p2.y,1},{*p3,p3.y,1})/2/det({p1.x,p1.y,1},{p2.x,p2.y,1},{p3.x,p3.y,1});
		p.y=-det({*p1,p1.x,1},{*p2,p2.x,1},{*p3,p3.x,1})/2/det({p1.x,p1.y,1},{p2.x,p2.y,1},{p3.x,p3.y,1});
		r=dis(p,p1);
	}
	friend istream&operator>>(istream&a,circle &b){return a>>b.p>>b.r;}
	friend ostream&operator<<(ostream&a,const circle &b){return a<<b.p<<' '<<b.r;}
	bool check(bector t){
		return dis(p,t)-r<=eps;
	}
	friend segment focus(const circle &a,const segment&b){
		auto d=b.project(a.p),u=b.p2-b.p1,e=u/~u;
		auto t=sqrt(a.r*a.r-sqr(dis(d,a.p)));
		return segment{d+e*t,d-e*t}.sort();
	}
	friend segment focus(const circle &a,const circle &b){
		auto d=dis(a.p,b.p),l=acos((sqr(a.r)+sqr(d)-sqr(b.r))/(2*a.r*d)),t=(b.p-a.p).arg();
		return segment{a.p+polar(a.r,t+l),a.p+polar(a.r,t-l)}.sort();
	}
	segment tangent(const bector&a){
		auto u=a-p;
		auto l=acos(r/~u);
		u=u*r/~u;
		return segment{p.rotate(p+u,+l),p.rotate(p+u,-l)}.sort();
	}
};
struct points:vector<bector>{
#define p (*this)
	points&read(istream&t,int n=-1){
		if(n==-1)cin>>n;
		p.resize(n);
		for(int i=0;i<n;i++)t>>p[i];
		return *this;
	}
	double area()const{
		// 无需满足多边形是凸的
		double ans=0;
		for(int i=1;i<siz(p)-1;i++)ans+=(p[i]-p[0])/(p[i+1]-p[0]);
		return ans/2;
	}
	points tobag(){
		sort(all(p));
		points b1,b2;
		b1.push_back(p[0]),b1.push_back(p[1]);
		for(int i=2;i<siz(p);i++){
			for(int j=siz(b1)-1;j>=1&&(b1[j]-b1[j-1]).leftis(p[i]-b1[j-1]);j--)
				b1.pop_back();
			b1.push_back(p[i]);
		}
		b2.push_back(p.back()),b2.push_back(p[siz(p)-2]);
		for(int i=siz(p)-3;~i;i--){
			for(int j=siz(b2)-1;j>=1&&(b2[j]-b2[j-1]).leftis(p[i]-b2[j-1]);j--)
				b2.pop_back();
			b2.push_back(p[i]);
		}
		reverse(all(b2));
		for(int i=siz(b1)-2;i;i--)
			b2.push_back(b1[i]);
		for(int i=0;i<siz(b2)-1;i++)
			if(b2[i]==b2.back()){
				b2.pop_back();
				break;
			}
		return b2;
	}
	double xzqk(){
		double ans=0;
		for(size_t i=0,j=1;i<p.size();i++){
			while((p[(i+1)%p.size()]-p[i])/(p[j]-p[i])<(p[(i+1)%p.size()]-p[i])/(p[(j+1)%p.size()]-p[i]))(++j)%=p.size();
			ans=max({ans,dis(p[i],p[j]),dis(p[(i+1)%p.size()],p[j])});
		}
		return ans;
	}
	circle incircle(){
		assert(p.size()==3);
		auto u=p[1]-p[0],v=p[2]-p[0];u=u/~u*~v;
		segment l1{p[0],p[0]+u+v};
		u=p[2]-p[1],v=p[0]-p[1];u=u/~u*~v;
		segment l2{p[1],p[1]+u+v};
		auto pp=focus(l1,l2);
		return circle{pp,dis(segment{p[0],p[1]},pp)};
	}
	circle outcirce(){
		assert(p.size()==3);
		auto u=p[1]-p[0],mid=(p[0]+p[1])/2;
		segment l1{mid,mid+u.rot90()};
		u=p[2]-p[0],mid=(p[0]+p[2])/2;
		segment l2{mid,mid+u.rot90()};
		auto pp=focus(l1,l2);
		return circle{pp,dis(p[0],pp)};
	}
	int contains(bector t){
		// 无需满足多边形是凸的
		// 2表示包含，1表示在边上，0表示在外面
		bool res=false;
		for(int i=0;i<siz(p);i++){
			auto a=p[i]-t,b=p[(i+1)%siz(p)]-t;
			if(!a.leftis(b)&&!a.rightis(b)&&!a.frontis(b))return 1;
			if(a.y>b.y)std::swap(a,b);
			if(a.y<eps&&eps<b.y&&a.leftis(b))res=!res;
		}
		return res?2:0;
	}
#undef p
};
circle mingenshin(points b){
	shuffle(all(b),rng);
	circle ans;
	for(int i=0;i<siz(b);i++)if(!ans.check(b[i])){
		ans={b[i],0};
		for(int j=0;j<i;j++)if(!ans.check(b[j])){
			ans=circle(b[i],b[j]);
			for(int k=0;k<j;k++)if(!ans.check(b[k])){
				ans=circle(b[i],b[j],b[k]);
			}
		}
	}
	return ans;
}
points a;
signed main(){
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	std::ios::sync_with_stdio(false);cin.tie(nullptr);
	cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(0);
	auto b=a.read(cin).tobag();
	cout<<siz(b)<<'\n';
	for(int i=0;;i++)if(b[i%siz(b)].y<=b[(i+1)%siz(b)].y){
		for(int j=i%siz(b);j<siz(b);j++)cout<<b[j]<<'\n';
		for(int j=0;j<i%siz(b);j++)cout<<b[j]<<'\n';
		break;
	}
	return 0;
}
accuracy: 1
time_ms: 70
memory_kb: 6992
final_rank: 11

code_snippet: #include<bits/stdc++.h>
#define siz(x) int((x).size())
#define all(x) std::begin(x),std::end(x)
#define fi first
#define se second
using namespace std;
using unt=unsigned;
using loli=long long;
using lolu=unsigned long long;
using pii=pair<int,int>;
mt19937_64 rng(random_device{}());
constexpr double eps=1e-8;
template<typename any>inline any sqr(const any &x){return x*x;}
inline int get_op(double x){if(fabs(x)<eps)return 0;return x>0?1:-1;}
double det(array<double,3>a0,array<double,3>a1,array<double,3>a2){
	double sum=0;
	sum+=a0[0]*a1[1]*a2[2];
	sum+=a1[0]*a2[1]*a0[2];
	sum+=a2[0]*a0[1]*a1[2];
	sum-=a0[2]*a1[1]*a2[0];
	sum-=a0[1]*a1[0]*a2[2];
	sum-=a0[0]*a1[2]*a2[1];
	return sum;
}
struct bector{
	double x,y;
	bool operator<(const bector&t)const{return fabs(x-t.x)<eps?y<t.y:x<t.x;}
	bool operator==(const bector&t)const{return fabs(x-t.x)<eps&&fabs(y-t.y)<eps;}
	bector(double a=0,double b=0):x(a),y(b){}
	double operator~()const{return sqrt(x*x+y*y);}
	double operator*()const{return x*x+y*y;}
	friend istream&operator>>(istream&a,bector&b){return a>>b.x>>b.y;}
	friend ostream&operator<<(ostream&a,const bector&b){return a<<b.x<<' '<<b.y;}
	friend bector operator+(const bector&a,const bector&b){return {a.x+b.x,a.y+b.y};}
	bector&operator+=(const bector&t){x+=t.x,y+=t.y;return *this;}
	friend bector operator-(const bector&a,const bector&b){return {a.x-b.x,a.y-b.y};}
	bector&operator-=(const bector&t){x-=t.x,y-=t.y;return *this;}
	friend bector operator*(const bector&a,double b){return {a.x*b,a.y*b};}
	friend bector operator*(double b,const bector&a){return {a.x*b,a.y*b};}
	bector&operator*=(double t){x*=t,y*=t;return *this;}
	friend bector operator/(const bector&a,double b){return {a.x/b,a.y/b};}
	bector&operator/=(double t){x/=t,y/=t;return *this;}
	friend double operator*(const bector&a,const bector&b){return a.x*b.x+a.y*b.y;}
	friend double operator/(const bector&a,const bector&b){return a.x*b.y-a.y*b.x;}
	bool frontis(const bector&t)const{auto r=*this*t;return get_op(r)>0;}
	bool backis(const bector&t)const{auto r=*this*t;return get_op(r)<0;}
	bool leftis(const bector&t)const{auto r=*this/t;return get_op(r)>0;}
	bool rightis(const bector&t)const{auto r=*this/t;return get_op(r)<0;}
	friend double dis(const bector&a,const bector&b){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}
	bector rot90()const{return {-y,x};}
	double arg()const{return atan2(y,x);}
	bector rotate(const bector&a,double r){
		auto u=a-*this;
		return *this+bector{u.x*cos(r)-u.y*sin(r),u.x*sin(r)+u.y*cos(r)};
	}
	friend double c0s(const bector&x,const bector&y){
		// 求 cos<x,y>
		return x*y/~x/~y;
	}
	bector unit()const{
		return (*this)/~(*this);
	}
	friend bool operator&(const bector&a,const bector&b){return !get_op(a*b);}
	friend bool operator|(const bector&a,const bector&b){return !get_op(a/b);}
};
bector polar(double p,double r){return {cos(r)*p,sin(r)*p};}
struct segment{
	bector p1,p2;
	segment(const bector&a={0,0},const bector&b={0,0}):p1(a),p2(b){};
	friend istream&operator>>(istream&a,segment &b){return a>>b.p1>>b.p2;}
	friend ostream&operator<<(ostream&a,const segment&b){return a<<b.p1<<' '<<b.p2;}
	friend bool operator&(const segment&a,const segment&b){return !get_op((a.p2-a.p1)*(b.p2-b.p1));}
	friend bool operator|(const segment&a,const segment&b){return !get_op((a.p2-a.p1)/(b.p2-b.p1));}
	friend bool banana(const segment&a,const segment&b){
		// 判断是否相交
		if(min(a.p1.x,a.p2.x)>max(b.p1.x,b.p2.x)||min(b.p1.x,b.p2.x)>max(a.p1.x,a.p2.x)||min(a.p1.y,a.p2.y)>max(b.p1.y,b.p2.y)||min(b.p1.y,b.p2.y)>max(a.p1.y,a.p2.y))return false;
		return get_op(((a.p2-a.p1)/(b.p1-a.p1))*((a.p2-a.p1)/(b.p2-a.p1)))<=0&&get_op(((b.p2-b.p1)/(a.p1-b.p1))*((b.p2-b.p1)/(a.p2-b.p1)))<=0;
	}
	friend bector focus(const segment&a,const segment&b){
		// 输入必须保证有交，输出交点
		bector v=a.p2-a.p1,w=b.p2-b.p1,u=a.p1-b.p1;
		return a.p1+v*((w/u)/(v/w));
	}
	friend double dis(const segment&a,const bector&b){
		if(a.p1==a.p2)return ~(b-a.p1);
		auto v1=a.p2-a.p1,v2=b-a.p1,v3=b-a.p2;
		if(v1.backis(v2))return ~v2;
		if(v1.frontis(v3))return ~v3;
		return fabs(v1/v2)/~v1;
	}
	friend double dis(const segment&a,const segment&b){
		// 求线段间最短距离
		if(banana(a,b))return 0;
		return min({dis(a,b.p1),dis(a,b.p2),dis(b,a.p1),dis(b,a.p2)});
	}
	bector project(const bector&a)const{
		// 求点 a 在当前线段上的投影点坐标
		if(a==p1||a==p2)return a;
		auto u=p2-p1,v=a-p1;
		return c0s(u,v)*(~v)*u.unit()+p1;
	}
	bector reflect(const bector&a)const{
		// 求点 a 在当前线段上的对称点
		return 2*project(a)-a;
	}
	segment&sort(){if(p2<p1)std::swap(p1,p2);return *this;}
};
struct circle{
	bector p;double r;
	circle(const bector&a={0,0},double b=0):p(a),r(b){}
	circle(bector p1,bector p2){
		p=(p1+p2)/2;
		r=dis(p1,p2)/2;
	}
	circle(bector p1,bector p2,bector p3){
		p.x=det({*p1,p1.y,1},{*p2,p2.y,1},{*p3,p3.y,1})/2/det({p1.x,p1.y,1},{p2.x,p2.y,1},{p3.x,p3.y,1});
		p.y=-det({*p1,p1.x,1},{*p2,p2.x,1},{*p3,p3.x,1})/2/det({p1.x,p1.y,1},{p2.x,p2.y,1},{p3.x,p3.y,1});
		r=dis(p,p1);
	}
	friend istream&operator>>(istream&a,circle &b){return a>>b.p>>b.r;}
	friend ostream&operator<<(ostream&a,const circle &b){return a<<b.p<<' '<<b.r;}
	bool check(bector t){
		return dis(p,t)-r<=eps;
	}
	friend segment focus(const circle &a,const segment&b){
		auto d=b.project(a.p),u=b.p2-b.p1,e=u/~u;
		auto t=sqrt(a.r*a.r-sqr(dis(d,a.p)));
		return segment{d+e*t,d-e*t}.sort();
	}
	friend segment focus(const circle &a,const circle &b){
		auto d=dis(a.p,b.p),l=acos((sqr(a.r)+sqr(d)-sqr(b.r))/(2*a.r*d)),t=(b.p-a.p).arg();
		return segment{a.p+polar(a.r,t+l),a.p+polar(a.r,t-l)}.sort();
	}
	segment tangent(const bector&a){
		auto u=a-p;
		auto l=acos(r/~u);
		u=u*r/~u;
		return segment{p.rotate(p+u,+l),p.rotate(p+u,-l)}.sort();
	}
};
struct points:vector<bector>{
#define p (*this)
	points&read(istream&t,int n=-1){
		if(n==-1)cin>>n;
		p.resize(n);
		for(int i=0;i<n;i++)t>>p[i];
		return *this;
	}
	double area()const{
		// 无需满足多边形是凸的
		double ans=0;
		for(int i=1;i<siz(p)-1;i++)ans+=(p[i]-p[0])/(p[i+1]-p[0]);
		return ans/2;
	}
	points tobag(){
		sort(all(p));
		points b1,b2;
		b1.push_back(p[0]),b1.push_back(p[1]);
		for(int i=2;i<siz(p);i++){
			auto u=b1.back()-b1[siz(b1)-2],v=p[i]-b1.back();
			while(u.leftis(v)){
				if(siz(b1)==1)break;
				b1.pop_back();
				u=b1.back()-b1[siz(b1)-2],v=p[i]-b1.back();
			}
			b1.push_back(p[i]);
		}
		b2.push_back(p.back()),b2.push_back(p[siz(p)-2]);
		for(int i=siz(p)-3;~i;i--){
			auto u=b2.back()-b2[siz(b2)-2],v=p[i]-b2.back();
			while(u.leftis(v)){
				if(siz(b2)==1)break;
				b2.pop_back();
				u=b2.back()-b2[siz(b2)-2],v=p[i]-b2.back();
			}
			b2.push_back(p[i]);
		}
		reverse(all(b2));
		for(int i=siz(b1)-2;i;i--)
			b2.push_back(b1[i]);
		return b2;
	}
	double xzqk(){
		double ans=0;
		for(size_t i=0,j=1;i<p.size();i++){
			while((p[(i+1)%p.size()]-p[i])/(p[j]-p[i])<(p[(i+1)%p.size()]-p[i])/(p[(j+1)%p.size()]-p[i]))(++j)%=p.size();
			ans=max({ans,dis(p[i],p[j]),dis(p[(i+1)%p.size()],p[j])});
		}
		return ans;
	}
	circle incircle(){
		assert(p.size()==3);
		auto u=p[1]-p[0],v=p[2]-p[0];u=u/~u*~v;
		segment l1{p[0],p[0]+u+v};
		u=p[2]-p[1],v=p[0]-p[1];u=u/~u*~v;
		segment l2{p[1],p[1]+u+v};
		auto pp=focus(l1,l2);
		return circle{pp,dis(segment{p[0],p[1]},pp)};
	}
	circle outcirce(){
		assert(p.size()==3);
		auto u=p[1]-p[0],mid=(p[0]+p[1])/2;
		segment l1{mid,mid+u.rot90()};
		u=p[2]-p[0],mid=(p[0]+p[2])/2;
		segment l2{mid,mid+u.rot90()};
		auto pp=focus(l1,l2);
		return circle{pp,dis(p[0],pp)};
	}
	int contains(bector t){
		// 无需满足多边形是凸的
		// 2表示包含，1表示在边上，0表示在外面
		bool res=false;
		for(int i=0;i<siz(p);i++){
			auto a=p[i]-t,b=p[(i+1)%siz(p)]-t;
			if(!a.leftis(b)&&!a.rightis(b)&&!a.frontis(b))return 1;
			if(a.y>b.y)std::swap(a,b);
			if(a.y<eps&&eps<b.y&&a.leftis(b))res=!res;
		}
		return res?2:0;
	}
#undef p
};
circle mingenshin(points b){
	shuffle(all(b),rng);
	circle ans;
	for(int i=0;i<siz(b);i++)if(!ans.check(b[i])){
		ans={b[i],0};
		for(int j=0;j<i;j++)if(!ans.check(b[j])){
			ans=circle(b[i],b[j]);
			for(int k=0;k<j;k++)if(!ans.check(b[k])){
				ans=circle(b[i],b[j],b[k]);
			}
		}
	}
	return ans;
}
points a;
signed main(){
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	std::ios::sync_with_stdio(false);cin.tie(nullptr);
	cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(0);
	auto b=a.read(cin).tobag();
	cout<<siz(b)<<'\n';
	for(int i=0;;i++)if(b[i%siz(b)].y<=b[(i+1)%siz(b)].y){
		for(int j=i%siz(b);j<siz(b);j++)cout<<b[j]<<'\n';
		for(int j=0;j<i%siz(b);j++)cout<<b[j]<<'\n';
		break;
	}
	return 0;
}
accuracy: 1
time_ms: 60
memory_kb: 7056
final_rank: 8

code_snippet: #include<bits/stdc++.h>
using namespace std;
const double eps = 5e-8;
#define eq(a,b) (fabs((a)-(b))<eps)
const double pi = acos(-1);
class point {
    public:
    double x,y;
    point(double x=0,double y=0):x(x),y(y){}
    
    point operator+(point b)const{return point(x+b.x,y+b.y);}
    point operator-(point b)const{return point(x-b.x,y-b.y);}
    point operator*(double b)const{return point(x*b,y*b);}
    point operator/(double b)const{return point(x/b,y/b);}
    //重载输入
    friend istream& operator>>(istream &in,point& a){in>>a.x>>a.y;return in;}
    friend ostream& operator<<(ostream &out,point a){out<<a.x<<" "<<a.y;return out;}
    double operator*(point b)const{return x*b.x+y*b.y;}//dot product
    double operator^(point b)const{return x*b.y-y*b.x;}//cross product
    bool operator==(point b)const{return eq(x,b.x)&&eq(y,b.y);}
    bool operator<(point b)const{return eq(x,b.x)?y<b.y:x<b.x;}
    double norm(){return x*x+y*y;}
    double abs(){return sqrt(norm());}
};
double norm(point a){return a.norm();}
double abs(point a){return a.abs();}

typedef vector<point> poly;
int contain(const poly &g,point p){//作平行x轴射线,交奇数次在内
    bool in = 0;
    int n = g.size();
    for(int i = 0;i < n;i++){
        point a = g[i] - p,b = g[(i + 1) % n] - p;
        if(fabs(a ^ b) < eps && (a * b) < eps)return 1;//on edge
        if(a.y > b.y)swap(a,b);//a under b
        if(a.y < eps && eps < b.y && (a ^ b) > eps)in ^= 1;//注意端点相交
    }return in ? 2 : 0;//in or out
}

inline double arg(point p){return atan2(p.y,p.x);}
point polar(double r,double theta){return point(r*cos(theta),r*sin(theta));}

struct seg{
    point p1,p2;
    point base(){return p2 - p1;}
    friend istream& operator>>(istream&in,seg&s){in>>s.p1>>s.p2;return in;}
};
typedef seg line;

bool isort(point a,point b){return eq(a * b,0);}//orthogonal
bool ispara(point a,point b){return eq(a ^ b,0);}//parallel

point project(seg s,point p){//点p在直线s上的投影
    point base = s.base();
    double r = (p - s.p1) * base / base.norm();//t / base / base
    return s.p1 + base * r;
}
point reflect(seg s,point p){
    return project(s,p) * 2 - p;//p + 2 * (project(s,p) - p);
}
double dislp(line l,point p){
    point base = l.base();
    return abs((base ^ p - l.p1) / abs(base));
}
double dis(seg s,point p){
    if((s.p2 - s.p1) * (p - s.p1) < eps)return abs(p - s.p1);
    if((s.p1 - s.p2) * (p - s.p2) < eps)return abs(p - s.p2);
    return dislp(s,p);
}
int ccw(point p0,point p1,point p2){
    point a = p1 - p0,b = p2 - p0;
    if((a ^ b) > eps)return 1;//counter clockwise
    if((a ^ b) < -eps)return -1;//clockwise
    if((a * b) < -eps)return 2;//p2--p0--p1
    if(a.norm() < b.norm())return -2;//p0--p1--p2
    return 0;//p0--p2--p1
}
poly andrew(poly s){
    const int n = s.size();
    if(s.size() < 3)return s;
    sort(s.begin(),s.end());
    poly u({s[0],s[1]}),l({s[n - 1],s[n - 2]});
    for(int i = 2;i < n;i++){
        for(int top = u.size();top >= 2 && 1 == ccw(u[top - 2],u[top - 1],s[i]);top--)u.pop_back();
        u.emplace_back(s[i]);
    }
    for(int i = n - 3;i >= 0;i--){
        for(int top = l.size();top >= 2 && 1 == ccw(l[top - 2],l[top - 1],s[i]);top--)l.pop_back();
        l.emplace_back(s[i]);
    }//clockwise
    reverse(l.begin(),l.end());
    for(int i = u.size() - 2;i >= 1;i--)l.emplace_back(u[i]);
    return l;
}
bool intersect(point p1,point p2,point p3,point p4){
    return ccw(p1,p2,p3) * ccw(p1,p2,p4) <= 0 && ccw(p3,p4,p1) * ccw(p3,p4,p2) <= 0;
}
bool intersect(seg s1,seg s2){
    return intersect(s1.p1,s1.p2,s2.p1,s2.p2);
}
double dis(seg s1,seg s2){
    if(intersect(s1,s2))return 0.0;
    return min({dis(s1,s2.p1),dis(s1,s2.p2),dis(s2,s1.p1),dis(s2,s1.p2)});
}
point getcross(seg s1,seg s2){
    assert(intersect(s1,s2));
    point base = s2.base();
    double d1 = abs(base ^ (s1.p1 - s2.p1));
    double d2 = abs(base ^ (s1.p2 - s2.p1));
    double t = d1 / (d1 + d2);
    return s1.p1 + s1.base() * t;
}

class circle{
public:
    point o; double r;
    circle(point o=point(),double r=0):o(o),r(r){}
    friend istream& operator>>(istream&in,circle&c){in>>c.o>>c.r;return in;}
    bool intersect(line l){
        return dislp(l,o) < r + eps;
    }
    bool intersect(circle c){
        vector<double> d = {abs(o - c.o),r,c.r};
        sort(d.begin(),d.end());
        return d[0] + d[1] > d[2] - eps;
    }
    pair<point,point> getcross(line l){
        assert(intersect(l));
        point pr = project(l,o);
        point e = l.base() / abs(l.base());
        double d = sqrt(r*r - (pr - o).norm());
        return {pr - e * d,pr + e * d};
    }
    pair<point,point> getcross(circle c){
        assert(intersect(c));
        double d = abs(o - c.o);
        double t = arg(c.o - o);
        double a = acos((r*r + d*d - c.r*c.r) / (2 * r * d));
        return {o + polar(r,t - a),o + polar(r,t + a)};
    }
};

int main(){
    int n;cin >> n;vector<point> g(n);
    for(auto& i : g)cin >> i;
    auto ans = andrew(g);cout << ans.size() << endl;
    rotate(ans.begin(),min_element(ans.begin(),ans.end(),[](point a,point b){
        return a.y < b.y || (eq(a.y,b.y) && a.x < b.x);
    }),ans.end());
    for(auto i : ans)cout << i << endl; 
}
accuracy: 1
time_ms: 130
memory_kb: 8360
final_rank: 16