3匹の猫

みみねこの競プロ精進メモ

AtCoder Regular Contest 115参加記(~C問題)

Aがわからない状態でウンコする最悪ムーブ
f:id:mmnkblog:20210322053311p:plain

A - Two Choices

この問題いつみても解けません。マジで苦手です。解説頑張って書こうとしたけど何もまとまりません。ごめんなさい。
愚直に解こうとするとO(N^2)なので何かしらの工夫が必要というところはわかりました。逆に言うとそれ以外なにもわかってません。(ブログ公開が遅れたのもこの問題のせい)

AtCoderの公式YouTubeチャンネルにあがっているすぬけさんの解説がわかりやすかったですが、あの解説を文章で書けないです。解説目当てで来た人はこちらを見てください。
youtu.be

B - Plus Matrix

100マス計算の内側から外側を復元できるか?という問題。

まずは判定問題を考える。
Aの各要素が定数だと仮定すると、行列CよりBも一意に定まる。これを言い換えれば、行列Ci行目の各要素からA_iを引けば、すべての行はBに一致するということである。つまり、もし解を構成できるならば、C_{i, j}からC_{i, j+1}までの増分をK_{i, j}として書き出すと、各行の増分は等しくなる(K_{i, j} = K_{i', j})。逆に、各行の増分が等しいなら、先ほどの議論よりABが存在することが言える。ただしこのままだと負整数も要素に含まれているので、数列の最小値を引いて0以上にする必要がある。

計算量は、O(N^2)

C - ℕ Coloring

こういう、2つの要素間に条件が発生するような問題は、N頂点の無向グラフを考えると良い性質が見えることがある。

i番目の頂点に整数iを書き込み、ijの約数なら頂点ijに辺を張る。このとき、問題文の条件は
「辺で結ばれた2頂点に異なる色を塗る必要があるときの、使用する色の数の最小化(とその色の塗り方)」
ということができる。これはグラフの彩色問題と呼ばれる有名な問題だが、NP困難(効率的に解くアルゴリズムが知られていないという意味のはず)の問題である。ということは、辺の貼られ方に特徴があるはずだ。

頂点1を最小の正整数である色1で塗ることとする。
頂点1は他の全ての頂点と隣接しているので、他の頂点が色1で塗られることはない。頂点2と頂点3は隣接していないので、それぞれの頂点を色2で塗る。この時、頂点2を除く2の倍数の頂点は色1でも色2でも塗れないことがわかる。3の倍数に関しても同じ。2のべき乗の頂点だけ考えると、頂点4は色3で、頂点8は色4で、…という風に、指数の増加に比例して色の種類数も増えていく。
つまり、ある頂点nn=p^x\times q^y\times r^z\times \dotsという風に素因数分解できるとき、頂点nの色は\max(x, y, z, \dots) + 1であることがわかる。これをN以下の整数すべてに対して求めてやればよい。これは例えばエラトステネスの篩を用いた素因数分解などでO(N\log N)で解くことができる。

感想

A問題よりB問題やC問題の方が簡単だと思います

AtCoder Beginner Contest 199(Sponsored by Panasonic)参加記(~C問題)

早解きしかできなかった
f:id:mmnkblog:20210424224915p:plain

A - Square Inequality

こういう、一見そのまま解くだけに見える問題を素直に実装すると、たいてい何らかの罠にはまる。たとえば、

  • 愚直に計算すると実行時間制限に間に合わない
  • 計算途中でオーバーフローする
  • 割り算や平方根の計算時に小数の誤差が発生する
  • コーナーケースが存在する(0除算など)

などが考えられる。今回は上記のどれにも当てはまらないので、単純に不等式評価を行うだけ。A問題なのでこれでACできる。
計算量はO(1)

B - Intersection

xの候補の最小値はA_{\min}、最大値はB_{\max}であるので、解xを全探索して条件を満たすかどうか全探索してカウントする。
計算量はO(N B_{\max})

ちなみに、別解が存在する。条件式より、解xの範囲はA_{\max}\le x \le B_{\min}となる。逆に、この範囲にある整数xはすべて条件を満たすので、答えはB_{\min} - A_{\max} + 1となる。
こちらの計算量はO(N)で、はじめの解法より高速。

C - IPFL

これは、先ほど挙げた罠のうち、「愚直に計算すると実行時間制限に間に合わない」問題。というか競プロの問題の9割がこれである。
クエリの数が最大で3\times 10^5個あるため、各クエリに対して高速に回答する必要がある。
クエリ1では、文字を交換するだけなので、計算量はO(1)である。
クエリ2では、長さNの文字列を交換するので、計算量はO(N)である。
なので例えば、すべてのクエリがクエリ2だった場合などを考えると、愚直な解法の計算量はO(NQ)となってしまいTLEする。

ところで、クエリ2を偶数回連続で処理すると元の文字列に戻る。連続で処理しなくても、クエリ1で操作されなかった文字S_iに関しては、クエリ2が偶数回処理されれば元の位置iに、奇数回処理されれば位置(i+N)\bmod {2N}に移動することがわかる。さらに、クエリ2を奇数回処理したあとにクエリ1を処理するのは、クエリ2での反転を一度無視して、クエリ1で与えられた値A, Bに対して文字S_{(A+N)\bmod {2N}}, S_{(B+N)\bmod {2N}}を交換した後にクエリ2を処理するのと同じである。
つまり、クエリ2は最後に一度だけ処理すればよく、クエリ1を処理する時点での「本来何回クエリ2を処理しているか」がわかれば、全体としてO(N+Q)の計算量で全てのクエリを処理できる。

感想

Dが難しかった

AtCoder Regular Contest 117参加記(~C問題)

600点問題が解けると嬉しい!!
f:id:mmnkblog:20210418231705p:plain

A - God Sequence

各要素が正の整数である二つの数列a, bを考えることにする。この時、数列aの要素の総和と数列bの要素の総和が等しくなれば良い。A=Bのとき明らかに、a_i = b_iとすることで条件を満たすことができる。

A\lt B(数列aより数列bの方が長い)と仮定する。(逆ならswapする)
この時数列bの要素をb_i = iとなるように決めると、数列aの最後の要素を除いてa_i = iとし、最後の要素をa_A = \sum_{k=A}^{B} kとすることで条件を満たすことができる。最後の要素で数値を調整するイメージ。

計算量はO(A+B)

B - ARC Wrecker

階数が同じビルがあった場合、どんな操作をしてもそれらのビルの階数は等しいままである。よって回数が同じビルはひとまとめにしておく。以降、すべてのA_iに重複が無いものとして考える。ついでに数列Aを昇順に並び替えておく。

Xとして選ぶ数字は数列Aの要素のどれかとしてよい。また、X=A_iとしたとき、少なくともA_iの値は1減るため、A_iは最大でもA_i回しか選べない。

実験するとわかるが、操作の順番は結果に関係しない。つまり「各iについて、X=A_iを何回選ぶか」という情報だけわかればよい。さらに、X=A_iとした場合、i \le jを満たすすべてのjについて、A_jの値は1減る。先ほど昇順にソートした理由はこれで、A_iが小さい順から「何回A_iを選ぶか」を決めていくことで数え上げることができる。

XA_ik回選ぶとすると、A_iA_{i+1}は共にkずつ減る。A_iの値が変わらないようなXA_{i+1}を選ぶ回数の最大値は、(A_{i+1} - k) - (A_i - k) + 1 = A_{i+1} - A_i + 1回である。この数値はkによらず一定である。
よって(A_1 + 1)\times \prod_{ i = 1 }^{N-1} (A_{i+1} - A_i + 1)が答え。計算量はソートがボトルネックとなり、O(N\log N)

C - Tricolor Pyramid

文字を選ぶ操作をなにかの演算として表現したい。各文字を0, 1,  2で置き換えて演算結果を表にまとめる。二つの数をa, bとしたとき、演算結果は -(a+b)\bmod 3と表せる。

左からi番目の整数をa_iとすると、最上段の整数に含まれる各a_iの個数は二項係数を用いて表せる。具体的に、最上段の整数は(-1)^{N-1} \times \sum_{i=1}^{N} ({}_{N-1} \mathrm{ C }_i \times a_i) \bmod 3である。なので素直にこの式を計算すればよいが、\bmod 3上の{}_{N-1} \mathrm{ C }_iの値を求める方法がわからなかった。

http://www.junko-k.com/collo/collo29.htm
上のサイトを見ながら考えたところ、N-13^kで割った余りで場合分けすることでO(\log N)で求められることに気が付いた。なのでその場合分けを丁寧に書いて計算し解答。計算量はO(N\log N)

ちなみにコンテスト後にTwitterを眺めていたところ、この方法は「Lucasの定理」という名前がついていて、3進数での各桁の数字をもとにして計算するらしい。コンテスト中に定理を導出した自分すげえ!(素直)

感想

最強学生コンで失ったレートをほとんど取り返したので満足。

第二回日本最強プログラマー学生選手権参加記(~E問題)

脳みそ、うごけー!(動かない)
f:id:mmnkblog:20210417182131p:plain

A - Competition

難しすぎないか…?最初に考えた方針が「両方の肉を同じだけ買ったときの金額で考える」ものだったが上手くいかず、一度B問題を解きに行って戻ってきた。スーパー高橋で売っている肉の1gあたりの価格はY\div Xで求められる。同様に、スーパーすぬけで売っている肉の1gあたりの価格は(解であるZg当たりの価格をAとすると)A\div Zで求められる。この二つの式の大小関係を式で表すと、
\dfrac{Y}{X} \gt \dfrac{A}{Z}
となる。さらに式変形して、
\dfrac{YZ}{X} \gt A
となる。この式の左辺は定数なので、Aは一通りに定まる。割り算の扱いに注意。計算量は、O(1)

B - Xor of Sequences

制約より、数列A, Bに含まれる整数の範囲は1以上1000以下である。この範囲に含まれる整数が解の条件を満たすかを全探索する。
数列に同じ整数が重複することはないという制約があるため、整数をxとすると、数列A, Bに含まれるxの個数がちょうど1個なら条件を満たしているといえる。(ちなみにこの処理を、整数xを見つけるたびに真理値型のフラグに対してtrueとのXORを取るという処理に変えると、最終的なフラグの値がそのまま解となる。)
これを1000回繰り返す。計算量は、O(N A_{\max})

C - Max GCD 2

ユークリッドの互除法での2数の関係から考えてみるとわかりやすい。
整数x, yの最大公約数がdであるとき、x, yはともにdの倍数である。A以上B以下の区間内にdの倍数が2個以上存在すれば、xyの最大公約数がdとなるような(x, y)を選ぶことができる。これは、「A以上の整数のうちdの倍数の最小値」にdを加えた値がB以下かどうかを判定すればよい。
dとして考えられる値の最小値は1、最大値は\dfrac{B}{2}であるので、制約よりこれらの間にある整数を全探索しても間に合う。計算量は、O(B)

D - Nowhere P

これ以降の問題は解けていない。
何か簡単な数式で解を表現できるらしいが、そこまで至らなかった。
コンテスト中の方針としては、「とても良い列」ではない列のほうをカウントして全体から引く作戦で考えていたが、数列を重複して数えてしまう沼にハマって抜け出せなかった。

E - Level K Palindrome

レベルKの回文として考えられる文字列の長さの最小値を考える。最小の長さを持つレベルKの回文は、レベル1の回文aを、間に何も挟まずに組み合わせていくことで作れる。この時の長さは、2^{K-1}である。よって、高橋君が持っている文字列の長さをNとすると、N \ge 2^{K-1}を満たしている必要がある。ついでにいうとK19以上だと確実にimpossibleである。
さらに、レベルKの回文を2つのレベルK-1の回文に分解するとき、Nが偶数なら2分割、Nが奇数なら間に一文字挟んで2分割、となるしかないので、その文字列を構成するレベルk0\le k \le K)の回文の長さは一意に定まる。レベル0の回文の長さも決まるが、このときどの文字を変えるのが最適なのかがよくわからなかった。レベル0の回文は回文であってはいけないという制約が邪魔だった。

感想

レートが50下がって気分も下がったが初めてお酒を飲んで全部忘れたのでOK

AtCoder Beginner Contest 198参加記(~E問題)

コーナーケースに気を付けろ!!
     
f:id:mmnkblog:20210411231102p:plain

A - Div

A君が得るお菓子の個数をA個、B君が得るお菓子の個数をB個とすると、B=N-Aという等式が成り立つ。この式から、Aを固定するとB1通りに定まること、A, Bがともに正の整数でなければならないことを考えると、Aが取りうる範囲1\le A\le N-1の中にある整数の個数、つまりN-1が答えとなる。計算量は、O(1)

ちなみに制約がゆるいので、brainfuckでも簡単なif文の処理が書ければ解ける。

B - Palindrome with leading zeros

問題文中の操作は、「Nを文字列としてみたときの末尾から連続している0をすべて削除する」と言い換えられる。こうすることで、操作後の文字列N'が回文かどうかを一度判定するだけで答えが求まる。N=0のときの処理に注意。計算量はO(\log N)

C - Compass Walking

2歩歩くことで、ユークリッド距離が2R以内の点に移動することができる。また、x歩以下で到達できる範囲は、原点から距離がxR以内の範囲である。このことから、解xを二分探索して求めることができる。
ただし、1歩歩くことで到達できるのは原点からちょうどR離れている点だけであるので、二分探索して得られた解がx=1だった場合にのみ、原点からの距離で場合分けする。

二分探索について。ルートを使うと小数誤差にやられる危険性があるので、整数のまま計算する。具体的には、距離の大小さえわかればよく、距離は負の値を取らないので、距離の2乗をそのまま比較する。式は以下の通り。

X^2 + Y^2 \le x^2 R^2

また、解の最大値は2\times 10^5程度と見積もれるが、距離の2乗で比較する方針だと上式の右辺が最大で4\times10^{20}となりオーバーフローするので、二分探索するときの解の最大値は2\times 10^5\div rとした。こうすると右辺は単にx^2となる。計算量はO(\log (X+Y))

コンテスト中はこれで十分だが、より厳密に議論すれば二分探索を用いなくてもO(1)で答えを求めることができる(公式解説より)。考察をサボれるときはサボることも大事。

D - Send More Money

条件がごちゃごちゃ書いてあるが、普通に覆面算を解くだけの問題。各英小文字に一桁の整数を割り当てるので、文字の種類数が11以上ある場合は解なし。

文字の種類数が10以下だった場合、各文字に整数を割り当てる方法を全通り試す。文字の種類数をaとすると、{}_10 \mathrm{ P }_a通り試せばよいことになる。C++のnext_permutation関数に長さ10の順列を与えてループを回し、順列の先頭a個だけをみて実際に文字に割り当て、N_1 + N_2 = N_3が成立するか確かめる。計算量はO(N!)

E - Unique Color

与えられるグラフは木であるので、2頂点間を結ぶパスは必ず1つだけ存在する。もちろんそのパスが最短である。また、木の頂点1から頂点xまでのパス上に頂点yが含まれていた場合、そのパスに頂点1から頂点yまでのパスが完全に含まれている。

以上のことより、頂点1からDFSをして「よい頂点」でない頂点を探すこととする。具体的には、DFSをしながら通った頂点の色をメモしていき、頂点xの色がすでに登場しているなら答えから外す、ということをしていけばよい。

メモするための道具として集合(set、挿入・削除がともにO(\log N))を用いたがTLEしてしまった。setのかわりにunordered_setを使ってみたが変わらず。しかもこの方法はメモリも大量に使うため非常に重い。
もう少し考えると、DFSで戻ってくるとき「どのタイミングで色cが使われなくなったか」は「どのタイミングで初めて色cが使われたか」の解と等しいため、setを使わず、bool配列で管理することができる。
計算量は、O(N)

F - Cube

むずすぎ

感想

青パフォだったのでうれしいけど、Eでの2ペナがもったいないと思った

キャディプログラミングコンテスト2021(AtCoder Beginner Contest 193)解法メモ(全問題)

タイトルが長すぎる

リアルタイムで参加できなかったコンテストのブログが抜けてて気になったので、暇なときに解き直してブログを書くことにしました。

A - Discount

算数。割合の計算は比を使った式で書くとわかりやすいと個人的に思う。
A:100 = B:x
と考えてもいいし、
A:B = 100:x
と考えてもいい。どちらにせよ
x = \dfrac{100B}{A}
という式になる。比を使った式の好きなところはこういうところ。

何パーセント引きか聞かれているので、100 - \dfrac{100B}{A}が答え。計算量はO(1)

B - Play Snuke

1軒しかお店が無いとする。このとき、A分かけてお店に着いた時、スヌケマシンの在庫は\max (X-A, 0)である。在庫が1以上のとき、つまりX-A1以上のときに限りP円でスヌケマシンを買うことができる。

お店がN軒に増えた場合でも各店舗での購入条件と購入金額はかわらない。また、どのお店を選んでも、その選択が影響することはないので、スヌケマシンを買うことができるお店のうち一番安く買えるお店での購入金額を求めればよい。計算量はO(N)

C - Unexpressed

事象Aとその余事象A^cの和は全事象\Omegaである。
a^bと表せないもの」 = 「全体」 -a^bと表せるもの」
なので、「a^bと表せるもの」の個数をカウントする。

a^bと表せるもの」の個数を上から評価する(最大値を見積もる)。
aを固定したとき、bを増やすとa^bも増加する。よって、1 \le a^b \le Nを満たすbの個数はb = \log_a Nである。
2 \le bに注意すると、aは最大で\sqrt N程度まで大きくなるので、全体の個数としては\sqrt N \times \log_2 N程度となる。(上から評価しているので、a=2として計算した。)
この値はN=10^5のときでもおよそ1.6 \times 10^6であるため、(a, b)の組を全探索しても実行時間制限に間に合うことがわかった。

カウントするときは重複やオーバーフローに注意して実装する。重複を避けるにはC++のsetなどを用いる。a=\lfloor N \rfloorで探索を打ち切る処理を忘れるとTLEするので注意。計算量はO(\sqrt N \log N)

D - Poker

かなり考えることが多いので順を追って考察する。

まず、「高橋君の勝つ確率」は\dfrac{高橋君が勝つパターンの数}{全てのパターンの数}で求められる。ここで言う「すべてのパターン」とは、すでに表向きになっていて裏向きのカードとして選ばれることのない8枚を除いた9K-8枚から2枚(高橋君の裏向きのカード1枚と青木君の裏向きのカード1枚)を選ぶ方法を指す。よって分母は{}_{9K-8} \mathrm{ C }_2となる。

高橋君に配られた裏向きのカードをx、青木君に配られた裏向きのカードをyと置く。高橋君が勝つパターンの数をカウントしたい。裏向きのカードが何の数かわかれば手札の点数を求めることができるが、ここで9K-8枚の中から2枚選ぶような全探索をするとO(K^2)の計算量となってしまうので実行時間制限に間に合わない。しかし、(x, y)の組み合わせとしては81通りしかないので、xyを決めたとき(x, y)が選ばれるのは何通りか求められれば、高橋君が勝つパターンの数も高速に求められる。

整数iが書かれたカードの残り枚数をC_iとする。x=yなら、C_x枚の中から2枚選ぶので{}_{C_x} \mathrm{C}_2 = C_x(C_x - 1)通りである。x\neq yなら、C_x枚の中から1枚、C_y枚の中から1枚それぞれ選ぶのでC_xC_y通りである。高橋君の手札の点数が青木君の手札の点数より高い場合にこの通り数をそのままカウントに加えることで、確率を求めることができる。

計算量は、手札の枚数をN、カードに書かれた整数の種類数をCとしてO(N+C^3)。この問題ではN=5, C=9であるため十分高速に動作する。

E - Oversleeping

電車の停止する区間は周期的に訪れ、\bmod (2X+2Y)上で一定である。高橋君の起きる区間も同様に、\bmod (P+Q)上で一定である。
ここで、中国剰余定理(CRT)を活用する。この定理は、
aで割るとp余り、bで割るとq余る整数x\bmod (\mathrm{lcm}(a, b))上にちょうど1つだけ存在する」
ということを言っている。さらに、この解となる整数xを構成する効率的なアルゴリズムが知られていて、O(\log(a+b+p+q))で動作する。実装には拡張ユークリッドの互除法を用いることが多いが、atcoder libraryに実装されているcrtをそのまま用いることもできる。

制約より、YQが小さいことがわかる。よって、電車が停止してからi秒後のタイミングと、高橋君が起きてからj秒後のタイミングが初めて一致するときがいくつか求め、(i, j)の組を全探索して最小値を求めればよい。具体的には、以下の2つの合同式を同時に満たす解xの最小値を、0\le i\lt Y, 0\le j\lt Qの範囲で全探索する。

x \equiv X+i \pmod{2X+2Y}
x \equiv P+i \pmod{P+Q}

計算量は、O(YQ\log(X+Y+P+Q))

F - Zebraness

まだ解けていないが、工夫することでフローの問題に帰着できるらしい。

感想

はじめてCRTを学んだが、そこまで難しくなくて食わず嫌いしていたなーと思った。ちなみにCRTは条件式が3本以上あっても解を求めることができる。

AtCoder Regular Contest 116参加記(~C問題)

今回は面白い問題だらけです。あ、既出問題を見つけられるということはその問題の解法を理解しているということだから、そんなに気にすることではないと思います。
f:id:mmnkblog:20210329011049p:plain

A - Odd vs Even

完全既出だったらしい。こんなことがあっていいのか?

いったん制約を無視して愚直に解いてみる。正整数を素因数分解する場合、愚直に\sqrt Nまで割っていく方法でO(\sqrt N)の計算量となる。約数を列挙する場合、各素因数ごとに何個選ぶかを考える。この時、Nの素因数に2が含まれていた場合、少なくともNの約数のうち半分は偶数である。

N=18 の例。

\begin{array}{c|c|c|c}
 \times & 3^0 & 3^1 & 3^2 \\
 \hline
 2^0 & 1 & 3 & 9 \\
 \hline
 2^1 & 2 & 6 & 18
\end{array}

さらにNの因数に4が含まれている場合、Nの約数のうち半分以上が偶数となる。逆に、Nの素因数に2が含まれていない場合、Nの約数が偶数となることはない。
よって、N2で割った余りと4で割った余りを見ることで答えが求まる。今回は大丈夫だが、素因数の話題が出てきたときは01の扱いに注意し、必要なら場合分けして対処する。

計算量は、O(T)

B - Products of Min-Max

整数列Aは順番を並べ替えても答えに影響しないので、昇順にソートする。こうすると、部分列Bの最大値と最小値を決めたとき、その間にある各要素に関してBに含めても含めなくても\max (B) \times \min (B)の値は変わらない。Bの最大値と最小値を全探索すると、計算量はO(N^2\log N)となる。
さらに、最小値を固定したときの式を書き出して\min (B)を括り出し、残った部分の式を他の最小値の場合と見比べると、その部分も効率的に更新できることがわかる。詳しい数式は公式解説に載っている。理解したいなら実際に式変形してみるのが早い。

よって、計算量をO(N \log N)に削減することができる。

C - Multiple Sequences

解けなかった。公式解説の数え上げによる解法がわかりやすかった。
また、問題文の条件に「A_i \neq A_{i+1}」を追加した小問題を考えると、動的計画法+数え上げで解けるらしい。この場合の計算量はO(M\log N)?になると思う。

感想

C問題をコンテスト中に解きたかった。解説を読んで理解できたので、まだまだ成長できそう。