Processing math: 65%

AtCoder Regular Contest 189 (Div. 2) B,C,D,E問題メモ

AtCoder Regular Contest 189 (Div. 2)

今回のARCから、簡単めのDiv.2と、難しめのDiv.1にわかれることになったらしい。
Div.2は、ABCのE~Gくらいがいっぱいある印象。

今まで以上にそれぞれのレート帯に適したコンテストに参加できるようになってうれしいね。

B - Minimize Sum

問題文

  • 数直線上に N 個のコマが置かれており、最初、すべてのコマは相異なる座標に置かれています。
    • 最初にコマが置かれている座標は X1,X2,,XN です。
  • 高橋君は、次の操作を繰り返し (0 回でも良い) 行う事ができます。
    • 1iN3 をみたす整数 i1 つ選ぶ。
    • 座標が小さい方から i 番目のコマのある位置と i+3 番目のコマのある位置の中点を M とする。
    • 座標が小さい方から i+1 番目のコマと i+2 番目のコマを、それぞれ M と対称な位置に動かす。
  • なお、本問題の制約下において、どのように操作を繰り返し行ってもすべてのコマが相異なる座標に置かれていることが証明できる。
  • 高橋君の目標は N 個のコマが置かれている座標の総和を最小にする事です。
  • 操作を繰り返した後の座標の総和としてあり得る最小の値を求めてください。

制約

  • 4N2×105
  • 0X1<X2<<XN1012
  • 入力はすべて整数

解法

変な操作は差分に着目してみる。定石の1つなのに思いつけなかったね。

             M
 |<---a--->|<-b->|<--c-->|
x0        x1    x2      x3
            ↓
 |<--c-->|<-b->|<---a--->|
x0      x1    x2        x3

差分に着目して操作を観察すると、3つ並んだ差分のうち、1番目と3番目をスワップすることに相当する。
つまり操作を繰り返すことで、偶奇別に、差分を好きな順に並び替えられる。

座標の総和を最小にするためには、なるべく左に小さい差分を持ってくるのがよい。
差分を偶数番目と奇数番目に分けてそれぞれでソートし、復元した結果が答えとなる。

Python3

C - Balls and Boxes

問題文

  • N 個の箱があります。
    • i=1,2,,N について、i 番目の箱には Ai 個の赤いボールと Bi 個の青いボールが入っています。
  • また、数列 (1,2,,N) を並べ替えた 2 つの順列 P=(P1,P2,,PN)Q=(Q1,Q2,,QN) が与えられます。
  • 高橋君は、下記の操作を好きな回数( 0 回でも良い)だけ繰り返すことができます。
    • 1iNを満たす整数 i を選び、i 番目の箱に入っているすべてのボールを箱から取り出して手に持つ。
    • 手に持っているすべての赤いボールを Pi 番目の箱に入れる。
    • 手に持っているすべての青いボールを Qi 番目の箱に入れる。
  • 高橋君の目標は、上記の操作の繰り返しによって、X 番目の箱以外にボールが入っていない状態にすることです。
  • この目標を達成することが可能かどうかを判定し、可能な場合はそのために行う操作回数の最小値を出力してください。

制約

  • 2N2×105
  • 0Ai,Bi1
  • 1Pi,QiN
  • P,Q はそれぞれ (1,2,,N) の順列
  • 1XN
  • 入力はすべて整数

解法

赤・青のボールを移し替える箱を示すそれぞれのグラフ(iPi,iQi に辺を張ったグラフ)は 置換グラフなので、いくつかのサイクルに分けられる。

i  1  2  3  4  5  6  7  8  9 10
P  1  4  9  5  8  2  3  6 10  7
Q  7  4  9 10  6  3  1  2  8  5
                                     ☆
P  ①┐  ②→④→⑤→⑧→⑥  ③→❾→⑩→❼    ●: ボールが入った箱
   ^┘  ^--------------┘  ^----------┘    ☆: ボールを集める箱(X)
                   ☆
Q  ①→⑦  ②→④→⑩→❺→❻→③→❾→⑧
   ^--┘  ^--------------------------┘

どちらか一方のグラフでも、☆を含むサイクル以外にボールが入った箱があったらアウト。

それ以外の場合、ボールが入っている箱のうち、☆まで一番遠い頂点から、順番に操作していくしかない。

P'  ❼→③→❾→☆
Q'  ❺→❻→③→❾→⑧→②→④→☆

基本的には2つの操作回数の和が答えとなるのだが、このうち、最長共通部分列の長さだけ操作を一度に行って節約できる。

    ⑦          __________
      ↘      /          ↘      最長共通部分列は ③,⑨,☆
        ③→⑨              ☆    ③と⑨に対する操作を、一度に行える
      ↗      ↘          ↗
⑤→⑥          ⑧→②→④

通常の最長共通部分列は、2つの列の長さを n,m として O(nm) かかるが、 「いずれかに同じ要素が出てこない」場合は、O(nlogm) などでおこなえる。

LISに言い換えられる。Q を、P での登場順に 1,2,... と振り直す(出てこない値はINF)と、 最長増加部分列がそのまま最長共通部分列となる。

P': 7,3,9,☆  →  1,2,3,4
Q':           →  INF,INF,2,3,INF,INF,INF,4

Python3

D - Takahashi is Slime

問題文

  • N 体のスライムが左右一列に並んでいます。
    • i=1,2,,N について、左から i 番目のスライムの大きさは Ai です。
  • K=1,2,,N のそれぞれの場合について、下記の問題を解いてください。
    • 初期状態で左から K 番目にいるスライムが高橋君です。
    • 高橋君が下記の行動を好きな回数( 0 回でも良い)だけ行ったあとの、高橋君の大きさとしてあり得る最大値を求めてください。
      • 高橋君に隣接するスライムのうち、高橋君より大きさが真に小さいものを選んで吸収する。
      • その結果、吸収されたスライムは消滅し、高橋君の大きさは吸収したスライムの大きさだけ増加する。

制約

  • 2N5×105
  • 1Ai109
  • 入力はすべて整数

解法

少しのメモ化の工夫をすれば、愚直に近いことをやっても意外と計算量が増えない。

K が小さい方から答えを求めていく。K=i の時の答えを求めるとする。

i から、1回毎に以下の法則に則って吸収する。

  • 左を吸収可能ならする。無理なら右を吸収可能ならする。それも無理なら終わり。

左のスライム j を吸収した場合、j を始点とした結果を再利用できないか確認する。
jj<i)から始めた場合の以下が記録してあるものとする。

  • 答え Bj
  • 吸収した最も左のスライム Lj
  • 吸収した最も右のスライム Rj

iRj の場合、Bi=Bj である。
j から始めて i を吸収可能」「i から始めて j を吸収可能」がともに成り立つ場合、両者から左右に吸収できる範囲は変わらない。

.....  j  ...  i  .....    jがi、または iがj を吸収した時点で、
      [---------]          ←①両者が確実に吸収している範囲
[---]                      ←②jだけが吸収しているかもしれない範囲
                  [---]    ←③iだけが吸収しているかもしれない範囲

ji 吸収前に②の範囲を吸収可能な場合、ij 吸収後に②を吸収可能である(i 吸収前の j より j 吸収後の i の方が必ず大きいので)。 ③の範囲にも同じ事が言えるので、結局、相手が自身吸収前に吸収可能な範囲は、自身も相手吸収後に吸収できる。
同じ途中状況に持っていくことができるため、答えも同じになる。

i>Rj の場合、少なくとも Lj まで、吸収するスライムの範囲を一気に左に伸ばせる。

これも、上記と似た考え方で「Sum(A[j:Rj])(あるいはそれ以下の範囲の合計)をもって j1,j2,...,Lj を吸収していけたのなら、それより大きい Sum(A[j:i]) をもってすれば必ず同じ範囲を吸収できる」といえる。

この2つの再利用により、全位置から探索を開始しても間に合うようになる。

計算量の見積もりは、少しあやふやだが、

左側への探索は、N 個の始点を通して O(N) 回。
あるスライムが、自身の右にあるスライムのうち何個から“左側への探索”の過程で吸収されるかを考えると、最初に1回吸収されたら以降はスキップされるので、O(1) 個となる。
あとは、「吸収を試みるが失敗」というのが、右側への探索で吸収が発生する分だけ追加で発生する。

右側への探索は、、、どう考えればいいだろうね。

「あるスライム j が、自身の左にある何個のスライムから、重ねて探索対象とされうるか」を考える。
i1<i2<...<ik<j なる i=(i1,...,ik) から探索されるとして、

  • i2i1 に吸収されているので、左への探索で i1 を吸収可能になったら、答えを再利用して終了する。
  • i3i2 に吸収されているので、左への探索で i2 を吸収可能になったら、答えを再利用して終了する。

でも、そうならずにいずれの ij まで探索が続いているということは、

  • Aik1sum(A[ik:j])
  • Aik2sum(A[ik1:j])
  • Aik3sum(A[ik2:j])
  • :
  • Ai1sum(A[i2:j])

が成り立つので、少なくとも i を遡るたびに2倍ずつになっていかないといけない。
よって、klogAmax で抑えられることが分かる。

1つのスライム当たり、右への探索対象となる回数は最大 \log{A_{\max}} より、全体でも O(N \log{A_{\max}}) となる(と思う)。

Python3

E - Straight Path

問題文

  • N 頂点の完全グラフ G の辺に正整数の番号を付ける方法のうち、以下の条件を満たすものを「良い完全グラフ」と言うことにします。
    • N 個の頂点を全てちょうど 1 回ずつ通るパスのうち、通った辺の番号を通った順に並べた数列が広義単調増加になるものが存在しない。
  • 「良い完全グラフ」が存在するか判定し、存在する場合は「辺に付ける正整数の番号の最大値」が最小になるものを 1 個構築してください。

制約

  • 2 \le N \le 20

解法

実験すると見えてくる。見えたら割と単純で、複雑な実装は要らない。

愚直は計算量がすごいことになるのが目に見えていて(実際、N=7 あたりで既に純粋な全探索は厳しいものがある)、 なかなかこの問題を本番中に実験しようとする勇気は出ないなあ。

小さい N の結果を観察して、ある程度「こうするのが良さそう」と法則を見いだして決め打ちしつつ、 少しずつ大きい N を試していく、ということを繰り返すと、N=4 および N \ge 6 では以下の構築が可能であるとわかる。

  • 頂点を半分に分け、同じグループ同士では“1”の辺を張る
  • 異なるグループ間には、後述の法則に従って“2”または“3”の辺を張る

最大値を3とした良い完全グラフが得られる。

最大値2が不可能という証明は、帰納法で示せる。
頂点数 n-1 のグラフに、ある値の割り振り方をした時、単調増加になるパスがあったとすると、 頂点 n を追加して既存の頂点との間の辺の値をどのようにしても、必ず n を含んだ単調増加パスに拡張できる。 N=3 などで、最大値2ではどのような値の割り振り方にも単調増加パスが存在することが全探索で示されるので、それ以上の頂点数のどのような値の割り振り方にも必ず含まれる。

N 偶数の時

GrpA  GrpB
  ○--○
GA  ×  GB      ※1,2,3の辺の内、2の辺のみ描画
  ○--○
 
Ga○--○Gb

2つの同数のグループ(GrpA,GrpB、上図では縦に並んだ3つずつが同一グループ)に分け、両グループから適当に1点ずつ選ぶ。(a,b
頂点集合を、以下のように命名する。

  • GrpA の a 1頂点からなる集合: G_{a}
  • GrpA の a 以外からなる集合: G_{A}
  • GrpB の b 1頂点からなる集合: G_{b}
  • GrpB の b 以外からなる集合: G_{B}

GrpA内、GrpB内の頂点同士は“1”の辺を張る。
異なるGrp間は、「G_aG_b」「G_AG_B」には“2”の辺を張る(上図で明示された辺)。
G_aG_B」「G_AG_b」には“3”の辺を張る。

「GrpA,GrpBの2つに分け、グループ内に“1”、グループ間に“2”以上の辺を張る」ということをした時点で、 「GrpAをいくつか巡る→GrpBに移る→GrpBをいくつか巡る」というパスが一切禁じられる。
よって、「GrpA→GrpB→GrpA→…」のように、グループ間を交互に渡り歩くパスしかなくなる。
(最初にGrpA内をいくつか巡ることもできない。交互に渡り歩くための個数が足りなくなるので)

たとえば G_A から開始した場合、どこかで G_a または G_b に移らなければならないが、 一度移ると、もう G_A-G_B 間、G_a-G_b 間は移動できなくなる。
たとえば G_A→G_b に移った場合、以降は G_AG_b を交互に移動するしかなくなり、G_a に移動できなくなる。
他の移動方法でも、3つめの頂点集合に移動した時点で、4つめには移動できなくなる。

このため、単調増加パスは不可能であることがわかる。

N 奇数の時

GrpA  GrpB
  ○--○
GA  ×  GB
  ○--○
  
  ○--○Gb
Ga  /
  ○

サイズが1だけ異なるように2つに分ける。
サイズが大きい方(GrpA)から2点、小さい方(GrpB)から1点選び、あとは偶数の時と同じように辺を張る。

偶数の時と違うのは、「最初に1回だけ、GrpA内の2点間を移動してから、GrpA-GrpB間の交互移動を開始することもできる」という点である。

そのため、G_a が1点しかない場合、最初にその1点から開始することで「G_a-(1)→G_A-(2)→G_B-(2)→...-(2)→G_A-(3)→G_b」のような単調増加パスが作れてしまう。
G_a を2点とすることで、これを防げる。

その他は偶数の時と同じ。

N=5 の時は、GrpAが3点しかなく、G_A,G_a をいずれも2点以上にすることができないため、コーナーケースとなる。
この場合は最大値が4まで必要であり、サンプルに答えがある(また、全探索しても十分高速である)。
まさか N=5 で最大値が最大になるとは思わなんだ。


構築はこのように単純なので、N=3000 くらいでも出力はできるのだが、 制約が N=20 と控えめなのは何でだろう。ジャッジが難しいからかな。(実際、どうやってるんだろ)

Python3

programming_algorithm/contest_history/atcoder/2024/1208_arc189.txt · 最終更新: 2024/12/11 by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0