🌳

[ABC126 E] 1 or 2

2024/01/25に公開

https://atcoder.jp/contests/abc126/tasks/abc126_e

木の数を求める問題。

A[X] と A[Y] の組み合わせ

各カードには整数 1 または 2 が書かれています。

とのことから A[X] と A[Y] の組み合わせは、

[1, 2].product([1, 2])  # => [[1, 1], [1, 2], [2, 1], [2, 2]]

の4パターンであることがわかる。

A[X] + A[Y] + Z = 偶数 から分かること

A[X] + A[Y] + Z = 偶数 の式に、その4パターンと、奇数の Z (1 でも 3 でも 5 でも結果は変わらなので仮に 1 とする) を代入すると、

(1 + 1 + 1).odd?  # => true
(2 + 2 + 1).odd?  # => true
(1 + 2 + 1).odd?  # => false
(2 + 1 + 1).odd?  # => false

成立するのは [1, 1][2, 2] の場合のみとわかる。

同様に偶数の Z (2 でも 4 でも 6 でも結果は変わらないので仮に 2 とする) で試してみると、

(1 + 1 + 2).odd?  # => false
(2 + 2 + 2).odd?  # => false
(1 + 2 + 2).odd?  # => true
(2 + 1 + 2).odd?  # => true

成立するのは [1, 2][2, 1] の場合のみとわかる。

したがって A[X] と A[Y] の組み合わせは次のようになる。

  • Z が偶数 → [1, 1] or [2, 2]
  • Z が奇数 → [1, 2] or [2, 1]

例2のシミュレーション

X Y Z
1 2 1
2 3 2
1 3 3
4 5 4
5 6 5

1ターン目

X Y Z = 1, 2, 1 を A[X] + A[Y] + Z に代入する。

A[1] + A[2] + 1

ここで Z の値に注目すると 1 が奇数なので、さっき出てきた、

  • Z が奇数 → [1, 2] or [2, 1]

に当てはまる。ということは、仮にここで A[1] をめくったとき、

  • 1 なら A[2] は 2
  • 2 なら A[2] は 1

だとわかる。同様に A[2] をめくったとき、

  • 1 なら A[1] は 2
  • 2 なら A[1] は 1

だとわかる。つまり、一方をめくれば一方がわかる。

この「めくり行為」を問題では「1コスト必要な魔法」と表現していて「最小何コストですべてのカードの中身を知ることができるか?」という問いなので、もし、(N = 2 でかつ) この入力例が1ターン目で終わっていれば「1めくり」で答えは 1 になる。

今回はめくらなかったことにして、コメントだけ入れて2ターン目に進む。

A[1] + A[2] + 1                 # A[1], A[2] は [1, 2] or [2, 1]

2ターン目

X Y Z = 2, 3, 2 なので、

A[1] + A[2] + 1                 # A[1], A[2] は [1, 2] or [2, 1]
A[2] + A[3] + 2

と追記して1ターン目と同様に Z に注目すると今度は偶数なので、

  • Z が偶数 → [1, 1] or [2, 2]

に当てはまり、A[2], A[3] は [1, 1] か [2, 2] だとわかったのでコメントしておく。

A[1] + A[2] + 1           # A[1], A[2] は [1, 2] or [2, 1]
A[2] + A[3] + 2           # A[2], A[3] は [1, 1] or [2, 2]

ここでまた仮に A[1] をめくってみるとどうなるか? 1ターン目で A[1] がわかれば A[2] も確定するのはわかった。その A[2] は2ターン目の式にも出てくるので、つまり A[1] がわかれば A[3] まで連鎖して確定する。たとえば、

A[1] が 1 の場合:

1 + 2 + 1
2 + 2 + 2

A[1] が 2 の場合:

2 + 1 + 1
1 + 1 + 2

したがって、(N = 3 でかつ) ここで入力が終わったとしても「1めくり」で答えは 1 である。

3ターン目

X Y Z = 1, 3, 3 なので、Z が奇数のときのパターンでコメントしておく。

A[1] + A[2] + 1           # A[1], A[2] は [1, 2] or [2, 1]
A[2] + A[3] + 2           # A[2], A[3] は [1, 1] or [2, 2]
A[1] + A[3] + 3           # A[1], A[3] は [1, 2] or [2, 1]

また、(N = 3 でかつ) ここで入力が終わったとした場合に、A[1], A[2], A[3] のどれをめくったとしても連鎖ですべてのカードが確定するので、答えは 1 になる。

4ターン目

X Y Z = 4, 5, 4 なので、Z が偶数のときのパターンでコメントしておく。

A[1] + A[2] + 1           # A[1], A[2] は [1, 2] or [2, 1]
A[2] + A[3] + 2           # A[2], A[3] は [1, 1] or [2, 2]
A[1] + A[3] + 3           # A[1], A[3] は [1, 2] or [2, 1]
A[4] + A[5] + 4           # A[4], A[5] は [1, 1] or [2, 2]

ここで仮に A[1] をめくると A[3] までは確定するが A[4] と A[5] には連鎖が届かないのがわかる。A[4] と A[5] を知るには、もう1めくり必要になる。したがって (N = 5 でかつ) ここで入力が終わったとした場合の答えは 2 になる。

5ターン目

X Y Z は 5, 6, 5 なので、Z が奇数のときのパターンでコメントしておく。

A[1] + A[2] + 1           # A[1], A[2] は [1, 2] or [2, 1]
A[2] + A[3] + 2           # A[2], A[3] は [1, 1] or [2, 2]
A[1] + A[3] + 3           # A[1], A[3] は [1, 2] or [2, 1]
A[4] + A[5] + 4           # A[4], A[5] は [1, 1] or [2, 2]
A[5] + A[6] + 5           # A[5], A[6] は [1, 2] or [2, 1]

ここで入力は終わりになる。これまでの法則からすると A[X] と A[Y] のどちらかが繋がっていると連鎖して確定するのがわかった。繋がっているグループは 1-2-3 と 4-5-6 の二つである。したがって、それぞれのグループに対してどれかを1枚めくれば全カードが確定するので最終的な回答は「2グループ」こと「2めくり」の 2 になる。

グループにまとめる

それには素集合データ構造が適しているので入力のペア

[1, 2]
[2, 3]
[1, 3]
[4, 5]
[5, 6]

だけを見て森を作る。そして木の数が答えになる。

Z の値はいらない?

カードの番号を知るには必要だが、連鎖するグループにまとめる段階ではいらない。(ここで疑問に感じる場合、例2のシミュレーションを再度確認する)

コード

N, M = gets.split.collect(&:to_i)                     # => [6, 5]
XYZ = M.times.collect { gets.split.collect(&:to_i) }  # => [[1, 2, 1], [2, 3, 2], [1, 3, 3], [4, 5, 4], [5, 6, 5]]
xy = XYZ.collect { |x, y, _| [x, y] }                 # => [[1, 2], [2, 3], [1, 3], [4, 5], [5, 6]]
p DisjointSet.new(1..N).unites(xy).tree_count         # => 2

参考

Discussion