目次

AtCoder Regular Contest 119 A,B,C,D,E問題メモ

AtCoder Regular Contest 119

ひっさびさに正のRating差分が得られた。

A - 119 × 2^23 + 1

A - 119 × 2^23 + 1

問題

解法

$b$ から探索していけば $2^{60}$ くらいで $10^{18}$ を超えるので、全部調べればよい。

Python3

B - Electric Board

B - Electric Board

問題

解法

操作前後で'0'と'1'の個数は変化しないので、個数が異なれば不可能。同じなら、操作回数はともかく端から埋めていけば必ず可能。
以下、個数は同じとする。

'1'は一つにまとめればまとめて移動させられるというのがトリッキーで、下手に細かな'1'の塊から動かすと、回数が増えてしまう。

また、'0'の連続はまとめては移動させられないため、以下は'1'を1回ずつ移動させる必要がある。

S 111110  1回でOK
T 011111

S 100000  5回必要
T 000001

端から順に'1'の位置を合わせていくことを考える。 '1'を何個ストックしてるか管理する。

【ストック無し】
S 1????  →  後のTに持っていくストックが1増える
T 0????      操作回数は1増える

S 0????  →  後のSから1持ってこないといけない(負のストック)
T 1????      操作回数は1増える

S 0????  1????  →  操作の必要は無い
T 0????  1????
【ストックあり】(たとえば3とする)
               ↓ストック分を移動させてきたらこうなってる
S ..1????  →  1111????    ここで'1'の塊を切り離して右に移動させる必要がある。
T ..0????      0????       操作回数は1増え、ストックは1増える

S ..0????  →  111????     操作回数は1増える
T ..0????      0????       ストックは変化無し

S ..0????  →  111????     操作の必要は無い
T ..1????      1????       ストックは1減る

S ..1????  →  1111????    操作の必要は無い
T ..1????      1????       ストックは変化無し
【負のストックあり】(たとえば-3とする)
    v 注目中の箇所   ↓負のストック分を考慮するとこうなってる(後の方の1を先取りしている状態)
S ..110011.. →  ..000001..    操作の必要は無い
T ..0?????..     ..0?????..    ストックは1増える

S ..110011.. →  ..100000..    Sの4つの1のうち、先の3つはより左にもっていかないといけない
T ..1?????.. →  ..1?????..    それにくっつけて移動させれば、
                               4つめの1をこの位置に持ってくるのは実質ノーコスト

S ..010111.. →  ..100000..    Sの4つの1のうち、先の3つはより左にもっていかないといけないが
T ..1?????.. →  ..1?????..    その際にSの'0'を飛び越えるのに1操作必要、ストックは1減る

S ..010111.. →  ..000001..    Sの4つの1のうち、先の3つはより左にもっていかないといけないが
T ..0?????.. →  ..0?????..    その際にSの'0'を飛び越えるのに1操作必要

ストックの正負と、$S,T$ の各箇所の組み合わせによって、答えを加算していけばよい。

Python3

よりよい解法

公式解説によると、「個数の少ない方に着目すると考察が進む場合がある」。なるほど。

今回、1回の操作で'0'は必ず1個しか含まれないため、'0'の方に着目する。

すると、操作は「'0'を、他の'0'を飛び越さない範囲で、移動させることができる」と言い換えられる。
つまり、'0'を互いに区別するとすると、$S→T$ でその順番は変わらない。左から順に対応させることになる。

i 0123456    0の場所(昇順)
S 1110001    [3, 4, 5]
T 1010101    [1, 3, 5]

'0'の移動については、

ので、結局「左から順に $S,T$ での'0'の位置を列挙したとき、異なっている箇所の数」が答えとなる。

例えば上記の例で、位置3は最初から両方とも'0'だが、互いに区別したときに2つは別々の'0'なので、移動が必要となる。
一方、位置5は同じ'0'なので、この場合のみ、移動の必要は無い。

C - ARC Wrecker 2

C - ARC Wrecker 2

問題

解法

隣り合った2つという操作を考えると、区間内の奇数番目と偶数番目のビルの高さの和が同じだったらよいとわかる。

区間の和と言えば累積和。
偶数と奇数に分けて累積和を取る。

A偶数        8     3     5     7     1  
A奇数           6     9     4     2     10

       0     8    11    16    23    24
          0     6    15    19    21     31

で、ある区間 $l,r$ を選ぶとき、計算がどうなるかというと、

                 [ l                 r]
       0     8    11    16    23    24         → 24 - 8 = 16
          0     6    15    19    21     31     → 21 - 6 = 15

加算される値(24,21)は必ず/か\に隣り合った2つとなる。減算される値(8,6)も同様。

で、この差が偶奇で等しければいいというのは、この隣り合った/\同士の差が等しければよいと言い換えられる。

                 [ l              r]
       0     8    11    16    23    24         → 23 - 8 = 15
          0     6    15    19    21     31     → 21 - 6 = 15
             ` 2 '            ` 2 '

なので、累積和の/\の差分を取って、同じ値となる組がどれだけ取れるか、という問題となる。

             0     8    11    16    23    24    
                0     6    15    19    21     31
累積和の差分  0   8  2  5 -4  1 -3 4  2  3  -7

常に「偶数側から奇数側を引いた値」とする点に注意

たとえば累積和の差分が $x$ となる箇所が $k$ 個あったら、その中から2箇所選ぶことが、区間を1つ決めることに対応する。
その場合の数は $\dfrac{k(k-1)}{2}$ で計算できる。

累積和の差分の値ごとに、これを足し合わせればよい。

Python3

D - Grid Repainting 3

D - Grid Repainting 3

問題

解法

上手いことしないと、赤マスの操作によって、同じ行や列にある他の赤マスが消えてしまう。

このように「グリッドで、行全体または列全体に対して操作する」問題では、行、列をそれぞれ1グループとした二部グラフで表現する解法がある。

ぶっちゃけ公式解説が親切なのでそれ読む。

連結成分毎に操作を求める。

合計 $N$ 頂点の二部グラフなら、最大の操作回数は $N-1$ 回となり、連結成分が表す行・列の全体の集合のどれか1方向の操作は諦めないといけない。 逆に言うと、$N-1$ 回の操作は上手くすると達成できる。

  012345678
0 _________    連結成分は、1,3行、2,4,6列の5頂点。
1 __R_R_R__    この内、どれか1つ以外の計4つの行・列は白く塗れる。
2 _________
3 __R_R_R__    行を残すならどの行を残しても同じ。
4 _________    列を残すならどの列を残しても同じ。

連結成分について、適当に根を決めて全域木をとってやると、とりあえずは根から葉に向かう方向に、その辺が表す赤マスを使えばよい。 ただし最後に調整が必要となる(後述)。

  0123
0 R___        _ :青マス
1 ____
2 ____
3 R_R_

    列0         辺が各赤マスを表す
   ↙  ↘
 行0    行3     (0,0) は行(X)方向
        ↓      (3,0) は行(X)方向
        列2     (3,2) は列(Y)方向 に使う

操作順に関しては葉から操作していくと、連結成分内の他の赤マスを邪魔しない。

つまり、最後に操作するのは根から伸びる1辺のうちのどれか1つ1)、ということになる。
この最後の1辺に関しては、XY方向を逆転させた方が多く白マスを塗り替えられる可能性がある。

  0123456789  
0 RRRRRR____            行0
1 __________        ↙↙↓↓↘↘
2 __________     列0,1, 2,3, 4,5

このままでは全てY方向に塗ることになってしまうが、
どれか1つはX方向に塗った方が明らかによい(最後の操作として)

しかし、結局どちらがよいのかは、他の連結成分の配置にも依存してくるので、すぐにはわからない。
なので、各連結成分につき最後の1操作だけは「保留」としておく。

  0123456789  
0 RRRRRR____    こんなんだったら、
1 __________    右下の連結成分も多くはY方向に塗ることになるので、
2 __________    X方向に塗る価値が下がる
3 ______RRRR

新規に白く塗り替えられるマスが行・列でいくつ残っているかを管理しながら、保留以外の操作を求めていく。

全ての連結成分を決め終えると、連結成分の個数分だけ、保留中の赤マスが残っている。

  0123456789  
0 Rxxxxx_xxx  x:白マス
1 _xxxxx_xxx  残っている非白マス  (行・列)=(4, 2)
2 _xxxxx_xxx
3 _xxxxxRxxx

この残った赤マスは、元々別々の連結成分だったことから互いに干渉することはないので、独立に、残っている白マスのうち多い方向の操作を選ぶことができる。

上記の例では、行方向に多く残っているので、両方ともY方向に操作したら4マスを新規に塗り替えられる、ということになる。

これで保留中の赤マスも確定し、答えが求められた。

Python3

E - Pancakes

E - Pancakes

問題

解法

式を立てて、絶対値を場合分けにより外す。

1回しかひっくり返さないので、ほとんどのパンケーキの両隣は変わらない。

変わるのは、区間を $[l+1,r]$ と決めたとすると、以下の通り。

     l [ l+1      r ] r+1
... 10    5  ...  8    3  ...    (10-5) と (8-3) が消え、
              ↓
... 10    8  ...  5    3  ...    (10-8) と (5-3) が追加される

この差分を比較して、最小のものを求めればよい。
1回もひっくり返さない場合は $0$ であり、どれだけ負側に絶対値を大きく出来るか、ということになる。

ただし、区間の左端が $l=0$ や、右端が $r=N-1$ の場合は、外側のパンケーキが存在しないため少し変わる。
この2つの場合は $O(N)$ でそれぞれ全探索できるので、それで処理する。

以下、区間の両端とも全体の端でない場合を考える。

絶対値が4つも出てくるが、大小関係を決め打って、そのうち2つを外してやる。

このうち2番目と3番目に関しては、0未満にならないため、考えても無駄とわかる。

1番目と4番目は $A_i$ と $A_{i+1}$ の大小関係で2つにグループ分けでき、

この2つを試せばよい。
(記号の付け方がまずかったが)この考え方では、$l,r$ といっても $l$ が必ず左に位置しなくてもよく、 単にペアの片方であればよいことが、$l,r$ を入れ替えても式の内容が変わらないことからわかる。

1番目のグループについて、ここでさらに、$A_l \le A_r$ を仮定する。
グループを $A_i$ 順にソートして、小さい方から順に $(l,r)$ ペアの $r$ とし、 既に処理済みのどれかとペアにすることを考えると、その状態が保たれる。

すると、

$A_{i+1}$ の最大値を順次更新していけば、$r$ を固定したときの $D_{l,r}$ の最小値が $O(1)$ で求まる。

全体では、グループサイズを $G$ として $O(G \log{G})$ でグループ内の最小値を求められる。ソートがボトルネックとなる。

同様の処理を4番目のグループについても行う。

これらの差分の最小値を、初期状態の見栄えの悪さと合わせれば答え。

Python3

$D_{i,j}$ の絶対値を外すとき、仮定する方を $(A_i,A_{i+1})$ 間の大小関係でなく $(A_l, A_r)$ 間としてしまうと、 同じグループに属する中での最小値を簡単に求めることができず、考察が進まなくなってしまった。

ここの組合せを変えてみるってのは思いついても良かったね。

1)
どれでもよい