Processing math: 68%

目次

AtCoder Regular Contest 148 B,C,D問題メモ

AtCoder Regular Contest 148

B - dp

B - dp

問題

解法

なるべく先頭から続く'd'を多くするのが正義。

「最初に出てきた'p'」の位置を、回転させる区間の左端 L に設定して問題ない。

dddppdppppd
   L

区間の右端 R は、以下の2通りが考えられる。

ddd ppp d pppp d pp dd pppp d
    L 2      1            1

2.が見落としがち?だが、こいつだけは「区間全てを'd'にする」ことができるので、その後に続く'd'を含めることができ、最適となる場合がある。

ddd pp dddddddddddddddddddddddddddddddd pppp d pppp d
    ~~
    ここだけをひっくり返すのがいい

可能性のある箇所を全て試しても、その箇所が O(|S|)、1回の判定で O(|S|) なので、O(|S|2) で通る。

Python3

C - Lights Out on Tree

C - Lights Out on Tree

問題

解法

言い換え

簡単な場合として木でなく直線だった時を考える。操作は以下のようになる。

このような操作は、隣り合う箇所との差分(同じか異なるか。XORと捉えてもよい)に注目すると「そこと1つ前の差分の状態のみを変更し、他は変えない」といえる。

    1 2 3 4 5 6 7
(1) 1 0 0 0 1 1 0    0:表 1:裏
      ^     ^   ^

従って操作回数は「表と裏が隣接する部分」の個数に一致する。
今回、最終的には全て裏にしないといけないが、 頂点 1 の親には必ず裏である頂点 0 が隠れていると考えると上手く表現できる。

木に戻しても似たようなことが言えて、1回の操作は 「自身と親との差分の状態のみを変更する」ので、答えはそのような箇所の個数と一致する。

答えの求め方

上記を踏まえると、以下のようなアルゴリズムが考えられる。

正しい答えは求まるが、これだとTLEとなる。

もし v がめっちゃ多くの頂点と隣接するハブ頂点で、Q 回のクエリでそればかり指定されると、 調べる u の個数が O(NQ) となってしまうからである。

高速化する。

ハブ頂点をたとえば「隣接頂点が N 個以上の頂点」として分類する。

さらに、各頂点の隣接リストも「ハブ頂点」「ハブ頂点以外」の2つに分類しておく。

木におけるハブ頂点の個数は少ないので、隣接する「ハブ頂点」はどの頂点も高が知れている。
隣接する「ハブ頂点以外」が、ハブ頂点は多い状態となっている。

こうすると、1回のクエリで辿る上限が O(|S|N)) となり、クエリ全体の |S| の総和を M として O(MN) となる。

(後から考えると、親だけ別に管理して「隣接頂点はとりあえず全て足す」「親も S に含まれたら双方で足しすぎなので2引く」でよかったね)

Python3

D - mod M Game

D - mod M Game

問題

解法

最終局面を考える

残った数が2つになった時、先攻がどちらを取っても負けてしまうのは、どのような状況だろうか?

これまでの和を a,b、残っている数を c,d とする。

a+c \equiv b+d \mod{M} かつ a+d \equiv b+c \mod{M} が同時に成り立つ。

整理すると、2c \equiv 2d \mod{M} となる。このような状況に後攻は持ち込みたい。

M が奇数の時

2c \equiv 2d \mod{M} となる (c,d) を考える。

2c,2d2M 未満なので、c \neq d の場合、2c+M=2d である(大小関係がどちらというのはあるが)。

M が奇数なら偶奇が合わないので明らかに矛盾しており、結局、c=d の場合しか無いことがわかる。

従ってそれまでの和 a,b \mod{M} も同じである必要があり、1つ少ない状態に帰着できる。

再帰的に考えると、後攻は「全ての数が偶数個ずつある場合のみ、先攻と同じ数を取り続けることで勝てる」ことがわかる。

M が偶数の時

c \neq d の場合でも 2c \equiv 2d \mod{M} に解があり得る。
M=6 のとき、(0,3),(1,4),(2,5) という、(i, i+\frac{M}{2}) という組が (c,d) になりえる。

このような組が残っていた場合、それまでの和 a,b も ちょうど \frac{M}{2} だけ離れていた場合に、両者の和 \mod{M} が同じとなる。
大小関係はどちらでもよい。

つまり、\frac{M}{2} だけ離れた組が (0,3,1,4) のように偶数個あれば、「0なら3、1なら4」のように先攻が選んだペアの片割れを選び続ければ、後攻が勝てると言うことになる。

もちろん、同じ数(c=d)がある場合はそれで打ち消してもいい。

\frac{M}{2} だけ離れている2数をペアとして、ペア毎に考えればよい。

個数カウントして、上記の通りに調べればよい。

p,q の偶奇が違うようなペアがあると先攻必勝という証明

数は偶数個あるので、どちらかが余るようなペアは偶数個存在するはずである。

この余る数を上手く組み合わせることで、和が同じになって後攻が勝ってしまわないか?
結論から言うと、それはない。

よって、

よって先攻必勝である。

Python3