ABC 139 F - Engines
偏角ソートして走査
N本のベクトルv1,...,vNを任意に組み合わせて長さ最大のものを作る問題。偏角が[θ,θ+π]の区間に入っているベクトルは方向 θ+π/2への長さの増加に寄与するので、v1,...,vNを偏角でソートしておいて、各区間[θ,θ+π]についてベクトルを加算して長さを比較すれば良い……のだけど、θは連続量なのでどのθを調べれば十分なのか考えなくてはならない。
以下、v1,...,vNが既に偏角でソート済みとする。各viの偏角を基点にして+πの範囲にあるベクトルを加算するという方針でだいたいは合っていそうだが、(0,10),(1,−1),(−1,−1)のようなケースでうまくいっておらず、(0,10)のみを使うパターンが漏れてしまう。180°の区間の両端がどのベクトルにも一致しないような、各偏角と偏角の「間」も基点として使う必要がある。
これを実現するには、各viの偏角に微小にプラスした地点から+πの範囲までの走査も追加すれば良さそう。上の例で言うなら、(1,−1)の偏角に0.000000001°足したところから+180°の範囲まで走査すれば、「間」のパターンを拾えている。ただ、実装にあたっては、いちいち微小な角度を足す必要はなく、viを基点にして+πの範囲まで走査してvi+vi+1+...+vkというベクトルが得られたら、基点のベクトルを除いたvi+1+...+vkも調べれば良い。
また、これだと各viから反時計回りの走査しかしていないので、時計周りにも調べる必要がある。各ベクトルのx成分をすべてマイナスに反転して鏡映にした上で、まったく同じ処理を適用するのが簡単だと思う。
計算量は、素朴に走査してO(N2)。尺取り法っぽくやれば走査の部分はおそらくO(N)にできて、その場合はソートが律速でO(NlogN)。
コンテスト中は漏れるパターンの拾い方を詰められなくて、走査範囲をπ/2からπまで雑に刻んで、通らなかったら乱択を加えるしかないと思っていたのだが、あっさり通ってしまった。偏角ソートしたら単に全区間をチェックすればよいということに気づけなかったのは反省。
凸包
原点のみの集合P0:={(0,0)}からスタートして、与えられたベクトルv1,...,vNによって移動可能な点を順に増やしていくとする:
Pi:=Pi−1∪{p+vi:p∈Pi−1}
PNの点の中で原点からの距離がもっとも大きいものを求めればよい。
問題はPiのサイズが指数的に増えることだが、凸包に含まれる頂点しか答えに影響しないので、毎回Piの凸包を取ればよい。凸多角形とその平行移動の凸包を取ると、高々2しか頂点が増えないので、Piのサイズが爆発しなくて済む。凸包がO(NlogN)で求まれば全体の計算量はO(N2logN)。