どう書く

オフラインリアルタイムどう書くE18の回答(2回目)

More than 1 year has passed since last update.

どう書くの後で書いた省メモリのO(n)版とO(1)版。
テストは省略。

O(1)のほうはangel_p_57さんの回答を参考にしています。

2つの三角形が含まれるxの値からyの最大最小を計算しています。

e18_a1.rb
def get_x_candidate( p, d, h )
  buf = [ p, ]
  if d == 0
    buf << p - h
    buf << p + h
  else
    buf << p + h * d
  end
  buf
end

def get_y_minmax( t, px )
  p, d, h = t
  if d.real == 0
    diff = (px - p.real).abs
    [ p.imag + h * d.imag, p.imag + diff * d.imag, ].sort
  else
    diff = (px - p.real) * d.real
    [ p.imag-diff, p.imag+diff, ]
  end
end

def solve( input )
  triangles = input.split('/').map { |s|
    /(?<x>\d+),(?<y>\d+)(?<d>[RLTB])(?<h>\d+)/ =~ s
    [ Complex(x, y), Complex('1i')**('LTRB'.index(d)), h.to_i-1, ]
  }
  x_range = triangles.map { |p, d, h|
    get_x_candidate( p.real, d.real, h ).minmax
  }.transpose

  x_range[0].max.upto(x_range[1].min).inject(0) { |sum, x|
    y_range = triangles.map { |t|
      get_y_minmax( t, x )
    }.transpose
    size = y_range[1].min - y_range[0].max
    (size >= 0) ? (sum + size + 1) : sum
  }.to_s
end

直角三角形を2つずつに分解して、組み合わせで共通部分を計算しています。

e18_a2.rb
I = Complex('1i')

def calc_tri( h )
  raise if h < 0
  (h+2)*(h+1)/2
end

def calc_same_dir( base_h, x, y, h )
  calc_x, calc_y, calc_h = x, y, h
  if calc_x < 0
    calc_h += calc_x
    calc_x = 0
  end
  if calc_y < 0
    calc_h += calc_y
    calc_y = 0
  end
  calc_h = base_h - calc_x - calc_y if calc_h > base_h - calc_x - calc_y
  return 0 if calc_h < 0
  calc_tri( calc_h )
end

def calc_reverse_dir( base_h, x, y, h )
  calc_x, calc_y, calc_h = x, y, h
  if calc_x > base_h
    calc_h -= calc_x - base_h
    calc_x = base_h
  end
  if calc_y > base_h
    calc_h -= calc_y - base_h
    calc_y = base_h
  end
  return 0 if calc_x < 0 or calc_y < 0 or calc_h < 0
  calc_h = calc_x + calc_y if calc_h > calc_x + calc_y
  ret = calc_tri( calc_h )
  ret -= calc_tri( calc_h - calc_x - 1 ) if calc_x < calc_h
  ret -= calc_tri( calc_h - calc_y - 1 ) if calc_y < calc_h
  ret -= calc_tri( calc_x + calc_y - base_h - 1 ) if base_h < calc_x + calc_y
  ret
end

def calc_alternate_dir( base_h, x, y, h )
  calc_x, calc_y, calc_h = x, y, h
  if calc_y < 0
    calc_h += calc_y
    calc_y = 0
  end
  if calc_x + calc_y > base_h
    calc_h -= calc_x + calc_y - base_h
    calc_x = base_h - calc_y
  end
  return 0 if calc_x < 0 or calc_y > base_h or calc_h < 0
  calc_h = base_h - calc_y + calc_x if calc_y + calc_h - calc_x > base_h
  ret = calc_tri( calc_h )
  ret -= calc_tri( calc_h - calc_x - 1 ) if calc_x - calc_h < 0
  if base_h < calc_x + calc_y + calc_h
    dh = calc_x + calc_y + calc_h - base_h
    if dh == 1
      ret -= calc_tri( 0 )
    elsif dh.even?
      ret -= calc_tri( dh/2 - 1 ) * 2
    else
      ret -= calc_tri( (dh+1)/2 - 1 )
      ret -= calc_tri( dh/2 - 1 )
    end
  end
  ret
end

def count( t1, t2 )
  npos = (t2[0] - t1[0]) * t1[1].conj
  nd = t2[1] * t1[1].conj
  case nd
  when 1
    calc_same_dir( t1[2], npos.real, npos.imag, t2[2] )
  when -1
    calc_reverse_dir( t1[2], npos.real, npos.imag, t2[2] )
  when I
    calc_alternate_dir( t1[2], npos.real, npos.imag, t2[2] )
  when -I
    calc_alternate_dir( t1[2], npos.imag, npos.real, t2[2] )
  else
    raise
  end
end

def solve( input )
  triangles = input.split('/').map { |s|
    /(?<x>\d+),(?<y>\d+)(?<d>[RLTB])(?<h>\d+)/ =~ s
    pos, d, h = [ Complex(x,y), I**('LTRB'.index(d)), h.to_i-1, ]
    center = pos + d * h
    sd = d * I
    [ [ center, -1*d, h, ], [ center+sd, sd, h-1, ], ]
  }

  sum = 0
  triangles[0].each { |t1|
    triangles[1].each { |t2|
      sum += count( t1, t2 )
    }
  }
  sum.to_s
end