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.Collections;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using static System.Math;
using System.Numerics;
static class Program{
	const int mod=(int)1e9+7;
	static void Main(){
		Sc sc=new Sc();
		int n=sc.I,p=0;
		var a=new int[n][];
		for(int i=0;i<n;i++){
			a[i]=sc.Ia;
			if(a[i][1]<a[p][1]){p=i;}
			else if(a[i][1]==a[p][1]&&a[i][0]<a[p][0]){p=i;}
		}
		Gs gs=new Gs(a,p);
		StringBuilder sb=new StringBuilder();
		sb.Append(gs.li.Count+"\n");
		for(int i = 0;i<gs.li.Count;i++) {sb.Append(gs.li[i].x+" "+gs.li[i].y+"\n");}
		Console.Write(sb);
	}
}
public class Gs{
	public class Dt{
		public int n;
		public long x,y;
		public double d;
		public Dt(double d,int n,long x,long y){this.d=d;this.n=n;this.x=x;this.y=y;}
	}
	private Dt[] arr;
	private double cd;
	private long cx;
	public List<Dt> li=new List<Dt>();
	public Gs(int[][] a,int p){
		int n=a.Length,q=2;
		arr=new Dt[n];
		for(int i=0;i<n;i++){
			if(i!=p){arr[i]=new Dt(Atan2(a[i][1]-a[p][1],a[i][0]-a[p][0])*180/PI,i,a[i][0],a[i][1]);cd=Max(cd,arr[i].d);}
		}
		arr[p]=new Dt(-512,p,a[p][0],a[p][1]);
		cx=a[p][0];
		Array.Sort(arr,Ca2);
		li.Add(arr[0]);
		li.Add(arr[1]);
		while(q!=n){
			li.Add(arr[q]);
			q++;
			while(li.Count>2&&(li[li.Count-1].x-li[li.Count-2].x)*(li[li.Count-3].y-li[li.Count-2].y)-(li[li.Count-1].y-li[li.Count-2].y)*(li[li.Count-3].x-li[li.Count-2].x)<0){
				li.RemoveAt(li.Count-2);
			}
		}
	}
	private int Ca1(Dt a,Dt b){
		if(a.d>b.d){return 1;}
		else if(a.d<b.d){return -1;}
		else if(a.x>b.x){return 1;}
		else if(a.x<b.x){return -1;}
		else if(a.y>b.y){return 1;}
		else if(a.y<b.y){return -1;}
		return 0;
	}
	private int Ca2(Dt a,Dt b){
		if(a.d>b.d){return 1;}
		else if(a.d<b.d){return -1;}
		else if(a.d==cd){
			if(cx==a.x){if(a.y<b.y){return 1;}else if(a.y>b.y){return -1;}}
			else if(cx>a.x){if(a.x>b.x){return 1;}else if(a.x<b.x){return -1;}}
			else{if(a.x<b.x){return 1;}else if(a.x>b.x){return -1;}}
		}
		else{
			if(cx==a.x){if(a.y>b.y){return 1;}else if(a.y<b.y){return -1;}}
			else if(cx<a.x){if(a.x>b.x){return 1;}else if(a.x<b.x){return -1;}}
			else{if(a.x<b.x){return 1;}else if(a.x>b.x){return -1;}}
		}
		return 0;
	}
	public double Diameter(){
		double q=(li[0].x-li[li.Count-1].x),p=(li[0].y-li[li.Count-1].y),d=Math.Sqrt(q*q+p*p);
		for(int i=1;i<li.Count;i++){
			q=(li[i].x-li[i-1].x);
			p=(li[i].y-li[i-1].y);
			d+=Sqrt(q*q+p*p);
		}
		return d;
	}
}

public class Sc{
	public int I{get{return int.Parse(Console.ReadLine());}}
	public long L{get{return long.Parse(Console.ReadLine());}}
	public double D{get{return double.Parse(Console.ReadLine());}}
	public string S{get{return Console.ReadLine();}}
	public int[] Ia{get{return Array.ConvertAll(Console.ReadLine().Split(),int.Parse);}}
	public long[] La{get{return Array.ConvertAll(Console.ReadLine().Split(),long.Parse);}}
	public double[] Da{get{return Array.ConvertAll(Console.ReadLine().Split(),double.Parse);}}
	public string[] Sa{get{return Console.ReadLine().Split();}}
	public object[] Oa{get{return Console.ReadLine().Split();}}
	public int[] Ia2{get{return Array.ConvertAll(("0 "+Console.ReadLine()+" 0").Split(),int.Parse);}}
	public int[] Ia3(int a){return Array.ConvertAll((a.ToString()+" "+Console.ReadLine()).Split(),int.Parse);}
	public int[] Ia3(bool a,int b,bool c,int d){return Array.ConvertAll(((a?b.ToString()+" ":"")+Console.ReadLine()+(c?" "+d.ToString():"")).Split(),int.Parse);}
	public long[] La2{get{return Array.ConvertAll(("0 "+Console.ReadLine()+" 0").Split(),long.Parse);}}
	public long[] La3(int a){return Array.ConvertAll((a.ToString()+" "+Console.ReadLine()).Split(),long.Parse);}
	public long[] La3(bool a,int b,bool c,int d){return Array.ConvertAll(((a?b.ToString()+" ":"")+Console.ReadLine()+(c?" "+d.ToString():"")).Split(),long.Parse);}
	public T[] Arr<T>(int n,Func<T> f){var a=new T[n];for(int i=0;i<n;i++){a[i]=f();}return a;}
	public T[] Arr<T>(int n,Func<int,T> f){var a=new T[n];for(int i=0;i<n;i++){a[i]=f(i);}return a;}
	public T[] Arr<T>(int n,Func<string[],T> f){var a=new T[n];for(int i=0;i<n;i++){a[i]=f(Console.ReadLine().Split());}return a;}
	public T[] Arr<T>(int n,Func<int,string[],T> f){var a=new T[n];for(int i=0;i<n;i++){a[i]=f(i,Console.ReadLine().Split());}return a;}
}