AtCoder problem 077

C++ solution:
#include <iostream>

#include <map>

#include <queue>

#include <vector>

#include <algorithm>

#include <atcoder/maxflow>

using namespace std;

#pragma warning (disable: 4996)



int N, T;

int AX[1 << 18], AY[1 << 18];

int BX[1 << 18], BY[1 << 18];

int dx[8] = { 1, 1, 0, -1, -1, -1, 0, 1 };

int dy[8] = { 0, 1, 1, 1, 0, -1, -1, -1 };



map<pair<int, int>, int> Map;

int Nex[1 << 18][8];

int Answer[1 << 18];



int main() {

	// Step #1. Input

	cin >> N >> T;

	for (int i = 1; i <= N; i++) scanf("%d%d", &AX[i], &AY[i]);

	for (int i = 1; i <= N; i++) scanf("%d%d", &BX[i], &BY[i]);

	for (int i = 1; i <= N; i++) Map[make_pair(BX[i], BY[i])] = i;



	// Step #2. Add Edge

	atcoder::mf_graph<int> Z(2 * N + 5);

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

		for (int j = 0; j < 8; j++) {

			int tx = AX[i] + dx[j] * T;

			int ty = AY[i] + dy[j] * T;

			Nex[i][j] = Map[make_pair(tx, ty)];

			if (Nex[i][j] != 0) {

				Z.add_edge(i, N + Nex[i][j], 1);

			}

		}

	}

	for (int i = 1; i <= N; i++) Z.add_edge(2 * N + 1, i, 1);

	for (int i = 1; i <= N; i++) Z.add_edge(i + N, 2 * N + 2, 1);



	// Step #3. Max Flow

	int res = Z.flow(2 * N + 1, 2 * N + 2);

	if (res != N) {

		cout << "No" << endl;

		return 0;

	}



	// Step #4. Get Answer

	for (auto e : Z.edges()) {

		if (e.flow == 1) {

			int a = e.to;

			int b = e.from;

			if (!(a >= N + 1 && a <= 2 * N)) continue;

			if (!(b >= 1 && b <= N)) continue;

			for (int k = 0; k < 8; k++) {

				if (Nex[b][k] == a - N) Answer[b] = k + 1;

			}

		}

	}



	// Step #5. Output

	cout << "Yes" << endl;

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

		if (i >= 2) cout << " ";

		cout << Answer[i];

	}

	cout << endl;

	return 0;

}

Crystal solution: class BipartiteMatching
  getter left : Int32, right : Int32

  def initialize(@left, @right)
    @graph = Array(Array(Int32)).new(left) { [] of Int32 }
    @left_match = Array(Int32?).new(left, nil)
    @right_match = Array(Int32?).new(right, nil)
    @used = Array(Bool).new(left, false)
  end

  def add_edge(l : Int32, r : Int32)
    @graph[l] << r
  end

  private def dfs(v : Int32) : Bool
    return false if @used[v]
    @used[v] = true
    @graph[v].each do |edge|
      if @right_match[edge].nil? || dfs(@right_match[edge].not_nil!)
        @left_match[v], @right_match[edge] = edge, v
        return true
      end
    end
    false
  end

  def solve : Int32
    while (0...left).reduce(false) { |update, i|
            update | (@left_match[i].nil? && dfs(i))
          }
      @used.fill(false)
    end
    left - @left_match.count(&.nil?)
  end

  def each_edge(&block) : Nil
    (0...left).each do |i|
      if j = @left_match[i]
        yield i, j
      end
    end
  end

  def each_edge
    (0...left).each.select { |i| @left_match[i] }.map { |i|
      {i, @left_match[i].not_nil!}
    }
  end
end

record Point, x : Int32, y : Int32 do
  def +(other : Point)
    Point.new(x + other.x, y + other.y)
  end

  def -(other : Point)
    Point.new(x - other.x, y - other.y)
  end

  def *(n : Int32)
    Point.new(x * n, y * n)
  end

  def //(n : Int32)
    Point.new(x // n, y // n)
  end
end

Direction = [
  {1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}, {-1, -1}, {0, -1}, {1, -1},
].map { |x, y| Point.new(x, y) }

n, t = read_line.split.map(&.to_i)
a, b = {0, 1}.map {
  Array.new(n) {
    x, y = read_line.split.map(&.to_i)
    Point.new x, y
  }
}

hash = b.zip(0..).to_h
g = BipartiteMatching.new(n, n)
a.each_with_index do |p, i|
  Direction.each do |dir|
    if j = hash[p + dir * t]?
      g.add_edge(i, j)
    end
  end
end

if g.solve == n
  puts "Yes", g.each_edge.join(' ') { |from, to|
    Direction.index((b[to] - a[from]) // t).not_nil!.succ
  }
else
  puts "No"
end