geam1113’s diary

主にAtcoderのコンテストの備忘録を書きます。AtCoder緑と水色を行ったり来たりしています。

AtCoder Biginner Contest 253 参加記録

コンテスト中AC:A〜E
NOMURA Programming Contest 2022(AtCoder Beginner Contest 253) - AtCoder

B - Distance Between Tokens

マンハッタン距離に関する問題です。
'o'である2箇所をS_{i,j},S_{i',j'}とすれば、答えは、

|i-i'|+|j-j'|

です。
Submission #32006932 - NOMURA Programming Contest 2022(AtCoder Beginner Contest 253)

C - Max - Min Query

多重集合SC++の多重集合multisetで解けます。

実装は後ほど記載のリンク参照。

WAを出したのですが、S.erase(i)S.erase(x)にしてしまってあり、こうするとSの中のxが全て削除されてしまいます。

補足ですが、Sに追加される個数が高々Q個なので、削除される回数も全体で高々Q回です。

Submission #32020504 - NOMURA Programming Contest 2022(AtCoder Beginner Contest 253)

D - FizzBuzz Sum Hard

基本的な包除原理です。
1からNまでの全体の和Sから不要なものを引くことを考えます。

1\leq i \leq NまでのA,Bの倍数であるiの和をS_A,S_Bとおきます。
S - S_A -S_Bとすると、Aの倍数かつBの倍数であるものを余分に1回引いてしまっています。

そこで、1\leq i \leq NまでのAの倍数かつBの倍数であるiの和をS_{AB}とおきます。
すると、S - S_A -S_B+S_{AB}で答えを求められます。

Aの倍数かつBの倍数であるiは、ABの最小公倍数をLとして、Lの倍数となります。よって、S - S_A -S_B+S_Lを求めればよいです。

具体的な計算方法です。
まず、Sは、\frac{N(N+1)}{2}です。

S_A,S_B,S_Lは以下のように求められます。
1\leq i \leq Nに含まれるXの倍数の個数をnとします。nは下式により求まります。
n=\lfloor \frac{N}{X} \rfloor - \lceil \frac{1}{X} \rceil +1

求めたいものは

S_X=X+2X+\cdots+nX

なので、

S_X=X(1+2+\cdots +n)=\frac{Xn(n+1)}{2}

で求められます。
Submission #32025795 - NOMURA Programming Contest 2022(AtCoder Beginner Contest 253)

E - Distance Sequence

A_iとしてあり得るのは1,...,Mなので、数列の状態数は、NM個であり、DPできそうです。

dp[i][j]:=数列Ai番目がjであるような数列の個数

このテーブルをi=1,...,Nの順に更新していくことを考えます。

遷移は、問題文の条件を考えると、

dp[i][j]\leftarrow (dp[i-1][1]+\cdots\ + dp[i-1][j-K]) + (dp[i-1][j+K]+\cdots\ + dp[i-1][M])

という感じになります(後の説明のために()で括ってあります)。
愚直に1個ずつ足していくと遷移に時間計算量O(M)かかり、全体でO(NM^2)となり、間に合いません。

そこで、遷移にかかる計算量を落とせないか考えてみます。
すると、
dp[i-1][1]+\cdots\ + dp[i-1][j-K]
及び
dp[i-1][j+K]+\cdots\ + dp[i-1][M]
は連続した区間の和となっています。  このような場合、累積和によって、計算量を落とせます。

具体的には、

dp_S [i][j]:=dp[i][0]+dp[i][1]+\cdots + dp[i][j]

とします。但し、便宜上dp_S[i][0]=0としておきます。

このようにすると、先程の遷移の右辺は、
dp[i-1][1]+\cdots\ + dp[i-1][j-K]=dp_S [i-1][j-K]
dp[i-1][j+K]+\cdots\ + dp[i-1][M]=dp_S [i-1][M] - dp_S [i-1][j+K-1]

という風になり、O(1)で計算できるようになります。

実装時、いくつか気をつける点があります。

  • j-K\lt 0となる可能性があるので、max(0,j-K)をとる。

  • j+K-1\gt Mとなる可能性があるので、min(M,j+K-1)をとる。

更に、上記2つでそのまま実装すると、K=0の時にコーナーケースとなります(dp[i][j]が2回足されてしまうはず)。
そのため、

  • K=0のときは、遷移をdp[i][j]\leftarrow  dp_S [i-1][M]とする。

このようにしておけばよいです。
Submission #32041363 - NOMURA Programming Contest 2022(AtCoder Beginner Contest 253)