AtCoder problem 041

C++ solution:
#include <iostream>

#include <vector>

#include <algorithm>

using namespace std;



struct Point {

	long long px, py;

};



Point operator+(const Point& a1, const Point& a2) {

	return Point{ a1.px + a2.px, a1.py + a2.py };

}



Point operator-(const Point& a1, const Point& a2) {

	return Point{ a1.px - a2.px, a1.py - a2.py };

}



bool operator<(const Point& a1, const Point& a2) {

	if (a1.px < a2.px) return true;

	if (a1.px > a2.px) return false;

	if (a1.py < a2.py) return true;

	return false;

}



// 点 p1 と p2 の外積を求める

long long crs(Point p1, Point p2) {

	return p1.px * p2.py - p1.py * p2.px;

}



long long gcd(long long a, long long b) {

	if (b == 0) return a;

	return gcd(b, a % b);

}



long long N;

Point G[100009];



int main() {

	// Step #1. 入力

	cin >> N;

	for (int i = 1; i <= N; i++) cin >> G[i].px >> G[i].py;

	sort(G + 1, G + N + 1);



	// Step #2. 凸包を求める

	vector<Point> G1, G2, Totsuhou;

	G1.push_back(G[1]); G2.push_back(G[1]);

	G1.push_back(G[2]); G2.push_back(G[2]);

	for (int i = 3; i <= N; i++) {

		while (G1.size() >= 2 && crs(G1[G1.size() - 1] - G1[G1.size() - 2], G[i] - G1[G1.size() - 1]) <= 0) G1.pop_back();

		while (G2.size() >= 2 && crs(G2[G2.size() - 1] - G2[G2.size() - 2], G[i] - G2[G2.size() - 1]) >= 0) G2.pop_back();

		G1.push_back(G[i]);

		G2.push_back(G[i]);

	}

	for (int i = 0; i < G1.size(); i++) Totsuhou.push_back(G1[i]);

	for (int i = (int)G2.size() - 2; i >= 1; i--) Totsuhou.push_back(G2[i]);



	// Step #3. 辺上の格子点の数を求める

	long long EdgePoint = Totsuhou.size();

	for (int i = 0; i < Totsuhou.size(); i++) {

		long long ax = Totsuhou[(i + 0) % Totsuhou.size()].px, ay = Totsuhou[(i + 0) % Totsuhou.size()].py;

		long long bx = Totsuhou[(i + 1) % Totsuhou.size()].px, by = Totsuhou[(i + 1) % Totsuhou.size()].py;

		long long vx = abs(bx - ax);

		long long vy = abs(by - ay);

		long long r = gcd(vx, vy);

		EdgePoint += (r - 1LL);

	}



	// Step #4. 多角形の面積（を 2 倍した値）を求める

	long long Area = 0;

	for (int i = 0; i < Totsuhou.size(); i++) {

		long long ax = Totsuhou[(i + 0) % Totsuhou.size()].px, ay = Totsuhou[(i + 0) % Totsuhou.size()].py;

		long long bx = Totsuhou[(i + 1) % Totsuhou.size()].px, by = Totsuhou[(i + 1) % Totsuhou.size()].py;

		Area += (bx - ax) * (by + ay);

	}

	Area = abs(Area);



	// Step #5. 答えを求める

	long long Answer = Area + EdgePoint + 2LL;

	cout << (Answer / 2LL) - N << endl;

	return 0;

}

Crystal solution: require "big"

module Geometric
  alias Real = BigFloat
  EPS = Real.new(1e-15)

  extend self

  struct Real
    def sgn : Int32
      self < -Geometric::EPS ? -1 : self > Geometric::EPS ? 1 : 0
    end
  end

  struct Vec2
    include Comparable(Vec2)

    property x : Real, y : Real

    def self.zero
      Vec2.new(Real.zero, Real.zero)
    end

    def initialize(x, y)
      @x, @y = Real.new(x), Real.new(y)
    end

    def +
      self
    end

    def -
      Vec2.new(-x, -y)
    end

    {% for op in %w[+ - * /] %}
      def {{op.id}}(other : Vec2)
        Vec2.new(x {{op.id}} other.x, y {{op.id}} other.y)
      end

      def {{op.id}}(other : Real)
        Vec2.new(x {{op.id}} other, y {{op.id}} other)
      end
    {% end %}

    def <=>(other : Vec2)
      {x, y} <=> {other.x, other.y}
    end

    def dot(other : Vec2)
      x * other.y - y * other.x
    end

    def cross(other : Vec2)
      x * other.y - y * other.x
    end
  end

  class Polygon < Array(Vec2)
    def initialize(points : Array(Vec2))
      initialize(points.size)
      concat points
    end

    def initialize
      super
    end

    def initialize(initial_capacity : Int)
      super
    end

    def initialize(size : Int, &block : Int32 -> T)
      super
    end

    def after(i : Int32) : Vec2
      self[i != size - 1 ? i + 1 : 0]
    end

    def area : Real
      (0...size).sum { |i|
        self[i].cross after(i)
      }.abs / 2
    end

    def convex_hull : Polygon
      result = Polygon.new
      points = sort
      points.each do |point|
        while result.size >= 2 && Geometric.ccw(result[-2], result[-1], point) != -1
          result.pop
        end
        result << point
      end
      t = result.size + 1
      points.reverse_each.skip(1).each do |point|
        while result.size >= t && Geometric.ccw(result[-2], result[-1], point) != -1
          result.pop
        end
        result << point
      end
      result.pop
      result
    end
  end

  # AB から見て BC が左に曲がる   : +1
  # AB から見て BC が右に曲がる   : -1
  # ABC, CBA の順に一直線上に並ぶ : +2
  # ACB, BCA の順に一直線上に並ぶ :  0
  # BAC, CAB の順に一直線上に並ぶ : -2
  def ccw(a : Vec2, b : Vec2, c : Vec2) : Int32
    x = (b - a).cross(c - a).sgn
    if x != 0
      x
    elsif (b - a).dot(c - b).sgn > 0
      2
    elsif (a - b).dot(c - a).sgn > 0
      -2
    else
      0
    end
  end
end

n = read_line.to_i
polygon = Geometric::Polygon.new((1..n).map {
  x, y = read_line.split.map(&.to_f)
  Geometric::Vec2.new(x, y)
}).convex_hull

on_line = (0...polygon.size).sum { |i|
  x = (polygon[i].x - polygon.after(i).x).abs.to_i64
  y = (polygon[i].y - polygon.after(i).y).abs.to_i64
  x.gcd(y)
}

puts (polygon.area + on_line / 2 - n + 1).to_i64