🌳

[ARC097/ABC097 D] Equals

2024/01/28に公開

https://atcoder.jp/contests/arc097/tasks/arc097_b

森を作って節と連番が同じ木か調べる問題。

整数のペアは添字かそれとも値か?

問題文の (x1, y1) が何を指しているのかがわかりにくい。こちらの解説によると「添字ではない」と書かれている。つまり値だということ。ところが、問題入力例1の説明には、

[5, 3, 1, 4, 2]

から 1 と 3 を入れ替えると、

[1, 3, 5, 4, 2]

になると書いてある。それは 1 と 3 を添字として解釈した場合と等しい。もし、1 と 3 の値を入れ替えると、

[5, 1, 3, 4, 2]

になるので例1の説明とは異なる。したがって、整数のペアは「添字」なのではないかと思われる。

問題を例1を使って単純化する

  • 5 3 1 4 2 を入れ替えたとき 1 2 3 4 5 と並びが一致する部分を最大にしたい
  • 入れ替えることができる位置は (1, 3) または (5, 4) 番目と決まっている

例1のシミュレーション

初期状態

p[] 1..N 同じ?
5 1
3 2
1 3
4 4
2 5

同じ箇所が 1 つあるので、ここで入れ替えることができない場合の答えは 1 になる。

まず (1, 3) 番目の入れ替え

p[] 1..N 同じ?
1 1
3 2
5 3
4 4
2 5

もう一カ所一致するようになった。

次に (5, 4) 番目の入れ替え

p[] 1..N 同じ?
1 1
3 2
5 3
2 4
4 5

これは、入れ替えない方がよかったとわかる。

つまり (1, 3) 番目を入れ替えた場合がもっとも多く2箇所に一致するので答えは2になる。

コード (WA)

以上を実装すると、

N, M = gets.split.collect(&:to_i)                    # => [5, 2]
P = gets.split.collect(&:to_i)                       # => [5, 3, 1, 4, 2]
XY = M.times.collect { gets.split.collect(&:to_i) }  # => [[1, 3], [5, 4]]
av = (1..N).collect.with_index { |e, i|
  if P[i] == e                  # 入れ替える前から同じ?
    true
  else
    XY.any? do |x, y|
      a = P.dup
      x -= 1
      y -= 1
      a[x], a[y] = a[y], a[x]   # 入れ替えてみて
      a[i] == e                 # 同じになるか?
    end
  end
}
av                                                   # => [true, false, false, true, false]
av.count(&:itself)                                   # => 2

となる。ところが、このコードでは例2と例3が通らない。何がいけなかったのか?

問題文には「好きなだけ交換してよい」とある。この言い回しがこの問題のもっとも重要な部分で、好きなだけ交換してよいということは、特定の値があっちこっちに移動するということでもある。隣同士の入れ替えに限定してはいないものの、パネポンのようなセルの操作を想像するとわかりやすい。

ただし「どこに何をどの順番で交換していけばよいか?」「最悪、何回目で交換を打ち切ればいいか?」などと考え出すと完全に詰んでしまう。

ここでは単に移動可能な位置を連鎖するようにグループ化していくだけでよい。仮に (1, 3) と (3, 5) 番目が交換可能であれば、1 番目の値が 5 番目まで移動できるのは明白なので、どこに何を持っていくかは重要でなく、1-3-5 番目がグループであることだけが重要になってくる。

コード (AC)

値ではなく添字(=番目)をグループ化している。

入力例3より
N, M = gets.split.collect(&:to_i)                    # => [10, 8]
P = gets.split.collect(&:to_i)                       # => [5, 3, 6, 8, 7, 10, 9, 1, 2, 4]
XY = M.times.collect { gets.split.collect(&:to_i) }  # => [[3, 1], [4, 1], [5, 9], [2, 5], [6, 5], [3, 5], [8, 9], [7, 9]]
ds = DisjointSet[XY]
P.each.with_index(1).count { |*e| ds.same?(*e) }     # => 8

参考

Discussion