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
x1 y1
x2 y2
:
xn yn

The first integer n is the number of points in P. The coordinate of the i-th point pi is given by two integers xi and yi.

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 ≤ xi, yi ≤ 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

Solution in C# (judge verdict: Accepted)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using static System.Console;
using static System.Math;
using static MyIO;
using static MyUtil;

using static Geometory;
using Vector = Geometory.Point;
using Line = Geometory.Segment;
using Polygon = System.Collections.Generic.List<Geometory.Point>;

public class A
{
	public static void Main() => (new Solver()).Solve();
}

public class Solver
{
	public void Solve()
	{
		int N = GetInt();
		var p = new Polygon();
		for(int i = 0; i < N; i++)
			p.Add(new Point(GetDouble(), GetDouble()));

		var s = AndrewScan(p, true);

		var min = s[0];
		var argMin = 0;
		for(int i = 1; i < s.Count; i++)
		{
			if(s[i].Y < min.Y || (Equals(s[i].Y, min.Y) && s[i].X < min.X))
			{
				min = s[i];
				argMin = i;
			}
		}

		var ans = new string[s.Count];
		for(int i = 0; i < s.Count; i++)
			ans[i] = $"{Round(s[(i + argMin) % s.Count].X)} {Round(s[(i + argMin) % s.Count].Y)}";

		WriteLine(s.Count);
		WriteLine(string.Join("\n", ans));
	}
}

public static class Geometory
{
	public const double EPS = 1e-10;
	public static bool Equals(double a, double b) => Math.Abs(a - b) < EPS;

	public class Point
	{
		public double X;
		public double Y;		
		public Point(double x, double y)
		{
			X = x;
			Y = y;
		}
	
		public static Point operator+(Point p, Point q) => new Point(p.X + q.X, p.Y + q.Y);
		public static Point operator-(Point p, Point q) => new Point(p.X - q.X, p.Y - q.Y);
		public static Point operator*(Point p, double a) => new Point(a * p.X, a * p.Y);
		public static Point operator*(double a, Point p) => new Point(a * p.X, a * p.Y);
		public static Point operator/(Point p, double a) => new Point(p.X / a, p.Y / a);
	}

	public class Segment
	{
		public Point P1;
		public Point P2;
		public Segment(Point p1, Point p2)
		{
			P1 = p1;
			P2 = p2;
		}
	}

	public class Circle
	{
		public Point C;
		public double R;
		public Circle(Point c, double r)
		{
			C = c;
			R = r;
		}
	}

	public static double Abs(Vector a) => Math.Sqrt(Norm(a));
	public static double Norm(Vector a) => a.X * a.X + a.Y * a.Y;
	public static double Dot(Vector a, Vector b) => a.X * b.X + a.Y * b.Y;
	public static double Cross(Vector a, Vector b) => a.X * b.Y - a.Y * b.X;

	public static bool IsOrthogonal(Vector a, Vector b) => Equals(Dot(a, b), 0.0);
	public static bool IsOrthogonal(Point a1, Point a2, Point b1, Point b2) => IsOrthogonal(a1 - a2, b1 - b2);
	public static bool IsOrthogonal(Segment s1, Segment s2) => Equals(Dot(s1.P2 - s1.P1, s2.P2 - s2.P1), 0.0);
	public static bool IsParallel(Vector a, Vector b) => Equals(Cross(a, b), 0.0);
	public static bool IsParallel(Point a1, Point a2, Point b1, Point b2) => IsParallel(a1 - a2, b1 - b2);
	public static bool IsParallel(Segment s1, Segment s2) => Equals(Cross(s1.P2 - s1.P1, s2.P2 - s2.P1), 0.0);

	public static Point Project(Segment s, Point p)
	{
		Vector b = s.P2 - s.P1;
		double r = Dot(p - s.P1, b) / Norm(b);
		return s.P1 + b * r;
	}
	public static Point Reflect(Segment s, Point p) => p + (Project(s, p) - p) * 2.0;

	public static double GetDistance(Point a, Point b) => Abs(a - b);
	public static double GetDistanceLP(Line l, Point p) => Math.Abs(Cross(l.P2 - l.P1, p - l.P1) / Abs(l.P2 - l.P1));
	public static double GetDistanceSP(Segment s, Point p)
	{
		if(Dot(s.P2 - s.P1, p - s.P1) < 0.0) return Abs(p - s.P1);
		if(Dot(s.P1 - s.P2, p - s.P2) < 0.0) return Abs(p - s.P2);
		return GetDistanceLP(s, p);
	}
	public static double GetDistance(Segment s1, Segment s2)
	{
		if(Intersect(s1, s2)) return 0.0;
		return Math.Min(Math.Min(GetDistanceSP(s1, s2.P1), GetDistanceSP(s1, s2.P2)),
		                Math.Min(GetDistanceSP(s2, s1.P1), GetDistanceSP(s2, s1.P2)));
	}

	public const int COUNTER_CLOCKWISE = 1;
	public const int CLOCKWISE = -1;
	public const int ONLINE_BACK = 2;
	public const int ONLINE_FRONT = -2;
	public const int ON_SEGMENT = 0;
	public static int Ccw(Point p0, Point p1, Point p2)
	{
		Vector a = p1 - p0;
		Vector b = p2 - p0;
		if(Cross(a, b) > EPS) 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;
	}
	public static 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);
	}
	public static bool Intersect(Segment s1, Segment s2) => Intersect(s1.P1, s1.P2, s2.P1, s2.P2);
	public static bool Intersect(Circle c, Line l) => GetDistanceLP(l, c.C) - c.R < EPS;
	public static bool Intersect(Circle c1, Circle c2) => GetDistance(c1.C, c2.C) - c1.R - c2.R < EPS;

	public static Point GetCrossPoint(Segment s1, Segment s2)
	{
		Vector b = s2.P2 - s2.P1;
		double d1 = Math.Abs(Cross(b, s1.P1 - s2.P1));
		double d2 = Math.Abs(Cross(b, s1.P2 - s2.P1));
		double t = d1 / (d1 + d2);
		return s1.P1 + (s1.P2 - s1.P1) * t;
	}
	public static Tuple<Point, Point> GetCrossPoints(Circle c, Line l)
	{
		if(!Intersect(c, l)) throw new Exception();
		Vector pr = Project(l, c.C);
		Vector e = (l.P2 - l.P1) / Abs(l.P2 - l.P1);
		double b = Math.Sqrt(c.R * c.R - Norm(pr - c.C));
		return Tuple.Create(pr + e * b, pr - e * b);
	}

	public static double Arg(Vector p) => Math.Atan2(p.Y, p.X);
	public static Vector Polar(double a, double r) => new Point(Math.Cos(r) * a, Math.Sin(r) * a);
	public static Tuple<Point, Point> GetCrossPoints(Circle c1, Circle c2)
	{
		if(!Intersect(c1, c2)) throw new Exception();
		double d = Abs(c1.C - c2.C);
		double a = Math.Acos((c1.R * c1.R + d * d - c2.R * c2.R) / (2 * c1.R * d));
		double t = Arg(c2.C - c1.C);
		return Tuple.Create(c1.C + Polar(c1.R, t + a), c1.C + Polar(c1.R, t - a));
	}

	/* IN: 2, ON: 1, OUT: 0 */
	public static int Contains(Polygon g, Point p)
	{
		int n = g.Count;
		bool x = false;
		for(int i = 0; i < n; i++)
		{
			Point a = g[i] - p;
			Point b = g[(i + 1) % n] - p;
			if(Math.Abs(Cross(a, b)) < EPS && Dot(a, b) < EPS) return 1;
			if(a.Y > b.Y) swap(ref a, ref b);
			if(a.Y < EPS && EPS < b.Y && Cross(a, b) > EPS) x = !x;
		}
		return (x ? 2 : 0);
	}

	public static Polygon AndrewScan(Polygon s, bool containsOnSegment = false)
	{
		s.Sort((a, b) => !Equals(a.X, b.X) ? a.X.CompareTo(b.X) : a.Y.CompareTo(b.Y));
		if(s.Count < 3) return s;

		var u = new List<Point>();
		u.Add(s[0]);
		u.Add(s[1]);
		for(int i = 2; i < s.Count; i++)
		{
			if(containsOnSegment)
			{
				for(int n = u.Count; n >= 2 && Ccw(u[n - 2], u[n - 1], s[i]) == COUNTER_CLOCKWISE; n--)
					u.RemoveAt(u.Count - 1);  // O(1) ?	
			}
			else
			{
				for(int n = u.Count; n >= 2 && Ccw(u[n - 2], u[n - 1], s[i]) != CLOCKWISE; n--)
					u.RemoveAt(u.Count - 1);  // O(1) ?				
			}
			u.Add(s[i]);
		}

		var l = new List<Point>();
		l.Add(s[s.Count - 1]);
		l.Add(s[s.Count - 2]);
		for(int i = s.Count - 3; i >= 0; i--)
		{
			if(containsOnSegment)
			{
				for(int n = l.Count; n >= 2 && Ccw(l[n - 2], l[n - 1], s[i]) == COUNTER_CLOCKWISE; n--)
					l.RemoveAt(l.Count - 1);  // O(1) ?	
			}
			else
			{
				for(int n = l.Count; n >= 2 && Ccw(l[n - 2], l[n - 1], s[i]) != CLOCKWISE; n--)
					l.RemoveAt(l.Count - 1);  // O(1) ?				
			}
			l.Add(s[i]);
		}
		
		l.Reverse();
		for(int i = u.Count - 2; i >= 1; i--)
			l.Add(u[i]);
		
		return l;
	}
}

public static class MyIO
{
	private static string[] args = null;
	private static int num = -1;
	private static int used = -1;

	private static string getArg()
	{
		if(used == num)
		{
			args = Console.ReadLine().Split(' ');
			num = args.Length;
			used = 0;
		}
		return args[used++];
	}

	public static int GetInt() => int.Parse(getArg());
	public static long GetLong() => long.Parse(getArg());
	public static double GetDouble() => double.Parse(getArg());
	public static decimal GetDecimal() => decimal.Parse(getArg());
	public static string GetString() => getArg();
	public static char GetChar() => getArg()[0];
	public static int[] GetInts(long N) => Enumerable.Range(0, (int)N).Select(_ => GetInt()).ToArray();
	public static long[] GetLongs(long N) => Enumerable.Range(0, (int)N).Select(_ => GetLong()).ToArray();
	public static double[] GetDoubles(long N) => Enumerable.Range(0, (int)N).Select(_ => GetDouble()).ToArray();
	public static decimal[] GetDecimals(long N) => Enumerable.Range(0, (int)N).Select(_ => GetDecimal()).ToArray();
	public static string[] GetStrings(long N) => Enumerable.Range(0, (int)N).Select(_ => GetString()).ToArray();
}

public static class MyUtil
{
	public static void chmax<T>(ref T x, T y) where T : IComparable<T>
	{
		if(x.CompareTo(y) < 0)
			x = y;
	}

	public static void chmin<T>(ref T x, T y) where T : IComparable<T>
	{
		if(x.CompareTo(y) > 0)
			x = y;
	}

	public static void swap<T>(ref T x, ref T y)
	{
		T tmp = x;
		x = y;
		y = tmp;
	}
}