Problem:
平面に長方形のシートが何枚か置かれている. これらのシートで覆われている部分の面積や周長を求めるプログラムを作成せよ.

ただし, 平面を座標平面とみなしたとき, シートの配置は次の条件 (1), (2) を満たすものとする.

(1) 各シートの長方形の 4 頂点の x, y 座標は全て 0 以上 10000 以下の整数であり, 長方形の各辺は x 軸, または y 軸に平行である.

(2) シートの枚数は高々 10000 枚以下である.

入力データ の 1 行目に長方形の個数 n と問題の種類を表す整数 r が空白で区切って書かれている. 2 行目以降の各行には, 各シートの左下頂点座標 (x1, y1) と右上頂点座標 (x2, y2) の座標値が x1, y1, x2, y2 の順で空白で区切って書いてある.

出力には r = 1 のとき 1 行目に面積を, r = 2 のとき 1 行目に面積, 2 行目に周囲の長さを出力する. いずれの場合も最後に改行を入れること.

また, テストデータの 40% では長方形の頂点の座標が 0 以上 100 以下で, そのうちの 1/2 では面積だけを求める問題である. さらに, 全体の 1/2 は面積だけを求める問題である.

入力例１

入力例２

入力例３

入力例４

5 1

5 2

2 2

3 2

0 0 3 2

0 0 3 2

0 0 8 9

2 2 8 8

1 1 2 5

1 1 2 5

0 0 9 8

3 0 4 9

0 4 6 5

0 4 6 5

5 0 7 9

3 3 5 6

3 3 5 6

5 0 7 6

5 0 7 6

　

出力例１

出力例２

出力例３

出力例４

29

29

80

45

38

36

36

入力

入力は複数のデータセットからなる．n, r がともに 0 のとき入力が終了する．データセットの数は 10 を超えない．

出力

データセットごとに, r = 1 のとき 1 行目に面積を, r = 2 のとき 1 行目に面積, 2 行目に周囲の長さを出力する.

入力例

5 1
0 0 3 2
1 1 2 5
0 4 6 5
3 3 5 6
5 0 7 6
5 2
0 0 3 2
1 1 2 5
0 4 6 5
3 3 5 6
5 0 7 6
2 2
0 0 8 9
0 0 9 8
3 2
2 2 8 8
3 0 4 9
5 0 7 9
0 0

出力例

29
29
38
80
36
45
36

上記問題文と自動審判に使われるデータは、情報オリンピック日本委員会が作成し公開している問題文と採点用テストデータです。

Solution in Ruby (judge verdict: Runtime Error)

class Line
  attr_reader :first, :last
  def initialize first, last
    @first, @last = first, last
  end

  # return lines
  def - other
    if first >= other.first && last <= other.last
      # hidden
      []
    elsif first < other.first && last > other.last
      # head & tail
      [Line.new(first, other.first), Line.new(other.last, last)]
    elsif first < other.first && last > other.first
      # head
      [Line.new(first, other.first)]
    elsif first < other.last && last > other.last
      # tail
      [Line.new(other.last, last)]
    else
      # all
      [self]
    end
  end

  # return lines
  def & other
    p_first = [first, other.first].max
    p_last = [last, other.last].min
    p_first < p_last ? [Line.new(p_first, p_last)] : []
  end

  def length
    last - first
  end

  def cover? i
    i >= first && i <= last
  end
end

class Rect
  attr_reader :x1, :y1, :x2, :y2
  def initialize x1, y1, x2, y2
    @x1, @y1, @x2, @y2 = x1, y1, x2, y2
  end

  def area
    x_line.length * y_line.length
  end

  # return rects
  def - other
    left_right = (x_line - other.x_line).map {|x_s_line|
      Rect.new(x_s_line.first, y1, x_s_line.last, y2)
    }.to_a

    up_down = (x_line & other.x_line).flat_map {|x_p_line|
      (y_line - other.y_line).map {|y_s_line|
        Rect.new(x_p_line.first, y_s_line.first, x_p_line.last, y_s_line.last)
      }
    }.to_a

    left_right.concat(up_down)
  end


  def x_line
    Line.new x1, x2
  end

  def y_line
    Line.new y1, y2
  end
end

class RectWithValidPerimeter
  attr_reader :rect
  attr_accessor :left_lines, :right_lines, :up_lines, :down_lines

  def initialize rect
    @rect = rect
    @left_lines = [Line.new(rect.y1, rect.y2)]
    @right_lines = [Line.new(rect.y1, rect.y2)]
    @up_lines = [Line.new(rect.x1, rect.x2)]
    @down_lines = [Line.new(rect.x1, rect.x2)]
  end

  def prune_valid_perimeter! other_rect, remain_when_same_line
    if other_rect.x_line.cover?(rect.x1) && (!remain_when_same_line || !(other_rect.x1 == rect.x1))
      #left
      @left_lines = prune_valid_lines @left_lines, other_rect.y_line
    end
    if other_rect.x_line.cover?(rect.x2) && (!remain_when_same_line || !(other_rect.x2 == rect.x2))
      #right
      @right_lines = prune_valid_lines @right_lines, other_rect.y_line
    end
    if other_rect.y_line.cover?(rect.y1) && (!remain_when_same_line || !(other_rect.y1 == rect.y1))
      #up
      @up_lines = prune_valid_lines @up_lines, other_rect.x_line
    end
    if other_rect.y_line.cover?(rect.y2) && (!remain_when_same_line || !(other_rect.y2 == rect.y2))
      #down
      @down_lines = prune_valid_lines @down_lines, other_rect.x_line
    end
  end

  def valid_perimeter
    [@left_lines, @right_lines, @up_lines, @down_lines]
      .flat_map {|lines| lines.map(&:length) }
      .reduce(0, :+)
  end

  private
  def prune_valid_lines valid_lines, deleter_line
    valid_lines.flat_map {|valid_line| valid_line - deleter_line }
  end
end

def calc_area rects
  extract_unique_rects([], rects.dup)
    .lazy
    .map(&:area)
    .reduce(0, :+)
end

def extract_unique_rects fixed_rects, remain_rects
  return fixed_rects if remain_rects.empty?
  return extract_unique_rects(
    fixed_rects.concat(fixed_rects.reduce([remain_rects.pop]) {|parts, fixed_rect|
      parts.flat_map {|part| part - fixed_rect }
    }),
    remain_rects
  )
end

def calc_perimeter rects
  units = rects.map {|r| RectWithValidPerimeter.new(r) }
  units.combination(2).each {|(a, b)|
    a.prune_valid_perimeter! b.rect, true
    b.prune_valid_perimeter! a.rect, false
  }
  units
    .lazy
    .map {|u| u.valid_perimeter }
    .reduce(0, :+)
end

Enumerator.new {|y|
  until ((n_r = gets.split(" ").map(&:to_i)) == [0, 0])
    y << [[method(:calc_area), method(:calc_perimeter)].take(n_r[1]), n_r[0].times.map { Rect.new(*gets.split(" ").map(&:to_i)) }]
  end
}
  .flat_map{|(resolvers, rects)| resolvers.map {|r| r[rects] } }
  .each {|r| puts r }