2020年9月14日月曜日

CodeChef September Challenge 2020 - Rotate the Polyline

vi=PiPi+1v_i=P_iP_{i+1}とした時、原点を通る直線llによってできた2つの(境界を含まない)半平面のそれぞれに連続した添え字のベクトルが並んでいれば、llに直交するベクトルが答えということになる。ただし、答えとなるベクトルは十分に原点に近い格子点を通る必要がある。

整数性の条件をいったん忘れると、直線llを表すベクトルの候補uuは各viv_iから偏角を左右に微小にずらした2N2N通りが考えられる。そこで偏角順の尺取り法をして、それぞれのuuから偏角が180°180 \degree以内におさまるすべてのviv_iの添え字を保持することにする。添え字がすべて連続している瞬間があればuu90°90 \degree回転したものを答えにすればよい。管理と判定はBITでできる。以下、実装のポイント:

  • 尺取り法で区間を伸ばす場合の処理: ベクトルの候補uuviv_iのクロス積がマイナスになるまで伸ばしていけばよい。[1]
  • 尺取り法で区間を縮める場合の処理: uuviv_iのクロス積がプラスになるまで縮めればよい。
  • uuと同一か180度回転した偏角が(vi)(v_i)に含まれる場合、uu(を90°90\degree回転したもの)は答えにできない。

さて、2N2N本の直線を用意する上で、整数性の条件を考慮しなくてはならない。単純化のために正数に限定して考える。ベクトル(x,y)(x, y)から反時計回りに微小に偏角を進めたベクトルを得ることは、y/x<1y/x < 1であればファレイ数列F2109F_{2\cdot 10^9}においてy/xy/xの直後にある分数を得ることと同じである。y/x1y/x \ge 1の場合も、逆数を取って直前の分数を得ると考えればよい。やり方を知らないのでググったらstackexchangeの質問が見つかった。ファレイ数列の隣接項p/q,c/dp/q, c/dcqdp=1cq-dp=1を満たすので、c,dc, dは互除法で求まるようだ。


  1. クロス積がマイナスになるベクトルが存在しなかった場合はこれだとうまく処理できないように見える。こういうことが起こるのは、偏角ソートされた(vi)(v_i)の隣接項同士が180°180\degreeより開いている場合だが、問題設定より全ベクトルを足すと00に戻るのでこの範囲に1つもベクトルがないということはありえない。 ↩︎