D - Congruence Points Editorial by kyopro_friends


\(O(N^3)\) 解法を解説します。

\(N=1\) のとき答えは明らかに Yes です。\(N\geq 2\)の場合を考えます。

\(S\) の点を \(S_1,\ldots,S_N\)\(T\) の点を \(T_1,\ldots,T_N\) と表すことにします。

\(S_1,S_2\) と対応する \(T\) の点の候補を全探索することを考えます。つまり、「\(S_1\)\(T_i\) に、\(S_2\)\(T_j\) に対応させ、さらに残りの \(S\) の点も \(T\) の点に対応させられるか?」という問題を全ての \((i,j)\) の組について考えます。答えが Yes となる \((i,j)\) の組が1つでも存在すれば元の問題の答えは Yes、1つも存在しなければ No となります。この問題の解法を考えます。

(なぜこのような問題を考えるかというと、回転移動と平行移動だけを用いて、ある2点を別の2点に移動させる方法は、(それが可能であるなら)1通りしかないので、「自由に操作できる」状態からたった1通りにまで選択肢を減らすことができるためです。特に、この新しい問題を「解く」とは、そのたった1通りの操作を見つけることを意味します)

\(S\) に平行移動と回転移動を施すことで \(T\) と一致させられるか?」という問題の代わりに、「\(S\), \(T\) にそれぞれ平行移動と回転移動を施すことで一致させられるか?」という問題を考えます。(許される操作は異なりますが、この2つは同じ問題になることに注意してください)

まず \(S_1,S_2\) 間の距離と \(T_i,T_j\) 間の距離が異なるなら答えは明らかに No です(\(S_1,S_2\) を同時に \(T_i,T_j\) に対応させることができないため)。そうでないときを考えます。

次の手順で判定できます。

  • 手順1:\(S_1\) が原点となるように \(S\) を平行移動し、\(T_i\) が原点になるように \(T\) を平行移動する
  • 手順2:\(S_2\)\(T_j\) と一致するように \(S\) を回転する
  • 手順3:\(S_3,\ldots,S_N\)\(T\) のいずれかの点と一致しているかチェックする

順に確認してみましょう。

手順1ではまず \(S_1\)\(T_i\) を一致させます。もちろん単に「\(S_1\)\(T_i\) と一致するように \(S\) を平行移動する」としてもよいですが、後の計算を簡単にするために、\(S_1,T_i\) が原点になるようにしています(\(S_1\)\(T_i\) をどこで一致させるかは答えに影響しません)。これ以降に平行移動を行うと \(S_1\)\(T_i\) がずれてしまうので、あとは回転移動だけを考慮すれば良いです。
続けて手順2で、\(S_2\)\(T_j\) を回転移動だけで一致させます。「どのくらい回転させればよいか」は、atan2関数により偏角を求め、その差を計算することで求めることができます。

偏角とはざっくりいうと、「\(x\) 軸から反時計回りに見て何度か?」を表す値です。イメージとしては「点 \(S_2\) は 7時の方向、点 \(T_j\) は11時の方向にあります。何度回せばいいでしょう?」「\(S\) を11-7=4時間分時計回りに回せば良い」というのをやっている感じです。詳細は各自で検索してください。
実際に点 \(S\) の点を回転させる操作は、回転行列を用いて計算することができます。詳しくは検索してください。
手順3は注意が必要です。単に「\(S\) の各点が \(T\) に含まれているか」をsetのinなどを使って判定すると誤差が原因でWAになります(浮動小数点数ではなく有理数を使うことで誤差なく計算することもできますがかなり大変です)。このため、「\(S\) の各点の十分近く\(T\) の点があるか?」という方法で判定する必要があります。一般の問題では、この「十分近く」の判定を適切に行うことは難しいですが、今回は入力座標が全て整数であり、座標の絶対値も小さいため、例えば「距離0.01以内」などで判定することができます。\(S\) の各点に対して、\(T\) の各点が近くにあるかを調べるので、計算量は \(O(N^2)\) になります。

以上により問題が解けました。計算量を考えてみましょう。

\(S_1,S_2\) を対応させる \(T\) の点の決め方が \(O(N^2)\) 通りあり、各問題を解く際には最後の一致判定で \(O(N^2)\) かかるので、一見すると全体で \(O(N^4)\) に見えます。 しかし、「\(S_1,S_2\) 間の距離と \(T_i,T_j\) 間の距離が異なるなら答えは No」という枝刈りにより、刈られないような \((i,j)\) の組は \(O(N)\) 組しか存在しないため、全体で \(O(N^3)\) で解けています(計算量解析は少し難しいため省略します)。

実装例(Python)

posted:
last update: