サントリープログラミングコンテスト2024(AtCoder Beginner Contest 357)G問題メモ
G - Stair-like Grid
問題文
- N 行の特殊な形をしたグリッドがあります。(N は偶数)
- 上から i 行目にはマスが左端から ⌈i2⌉×2 マス並んでいます。
- 上から i 行目、左から j 列目のマスを (i,j) と表します。
N = 6 □□ □□ □□□□ □□□□ □□□□□□ □□□□□□
- 各マスは空きマスと壁マスのいずれかです。壁マスは M 個あり、i 個目の壁マスは (ai,bi) にあります。ただし (1,1) と (N,N) は空きマスです。
- (1,1) を出発して、右または下に隣り合う空きマスへの移動を繰り返して (N,N) へ辿り着く方法は何通りありますか?答えを 998244353 で割った余りを求めてください。
制約
- 2≤N≤2.5×105
- N は偶数
- 0≤M≤50
- 1≤ai≤N
- 1≤bi≤⌈ai2⌉×2
- (ai,bi)≠(1,1) かつ (ai,bi)≠(N,N)
- i≠j ならば (ai,bi)≠(aj,bj)
解法
普通のグリッドの場合
公式Editorialにもあるが、通常の H×W のグリッド中に(2乗が通る程度の)M 個の壁マスがある、という問題であれば、EDPC Y - Grid 2 で既出。
全体から壁マスを通過する経路を引く。壁マスも通過できるとして、(確実に)踏んだ壁マス数により包除原理DPをおこなう。
- DP仮[i,k]= いま壁マス i にいて、これまでに踏んだ壁マス数が k 個以上の場合の経路数
さらに、「壁マス i にいる状態から、次に壁マス j に行く」という遷移は、これまでに踏んだ黒マス数 k に依らず一定である。
(共通して、i→j への経路数がかかる)
こういう場合、個数すら省略して、偶奇(正にかかるか負にかかるか)だけにまとめてしまえる。
- DP[i]= いま壁マス i にいる場合の、(踏んだ黒マスが偶数の場合の経路数)-(奇数の場合の経路数)
計算しやすいグリッドにして、壁マスを追加
\ ○\ ○: 追加した空きマス(始点・終点) □□ □□\ ■: 追加した壁マス □□ → □□■\ □□□□ □□□□\ □□□□ □□□□■\ □□□□□□ □□□□□□\ □□□□□□ □□□□□□○\
N+2 の階段状のグリッドにして、i=j のナナメラインのマスは踏んではいけないとする。
また、2段に1段、一番右のマスは踏んではいけないとする。
この上で、左上の○→右下の○の経路数は、元の問題と同じである。
こういうグリッドの (i1,j1)→(i2,j2) の経路数は、カタラン数や鏡像法を使って、階乗の事前計算のもとで O(1) で求められる。
2点間の経路数が高速に計算できるなら、EDPC Y と同等の手法が使える。。。
のだが、全ての壁マスを同等に扱うと O((N+M)2) かかるのでTLE。
本来の壁マスと異なり、追加した壁マス(「角マス」とする)同士の遷移は規則的になることを利用して、分割統治FFTをする。
ある角マス i に対して 0~i−1 の角マスのDPが分かっている場合、 角マス間の遷移はカタラン数の偶数番目 c2,c4,c6,... を、DP[i−1],DP[i−2],... と掛け合わせたものの合計となる。
○----- x132-, DP[i-3] x 132 □□ | □□■-- x14-| DP[i-2] x 14 □□□□ | □□□□■x2-| + DP[i-1] x 2 □□□□□□ v --------------- □□□□□□■ DP[i]
こういう形のDPは、分割統治FFTで O(Nlog2N) で求められる。
あとは、本来の壁マスの遷移を、分割統治の間に挟み込む。
角マスのDPを DP1、壁マスのDPを DP2 のように、分けて管理する。
「遷移元となるのは、値が確定してもう更新されないDP」(そうしないと、遷移元が更新されるたびに遷移先も更新が発生する)という点に注意すると、
角マスの DP1[i] が確定するのは、分割統治FFTで [i,i+1) の処理に来たときである。
そのタイミングで、壁マスに関する遷移を計算する。
確定した角マス i からは、そこから右下の壁マスに遷移できる ○ □□ i □□■ ×: DP1[i] が確定した際、i からの遷移を計算する壁マスの範囲 □□×× □□××■ □□×××× □□××××■ i からの遷移を計算し終えたら、i の下にある壁マスも(壁マス同士を除いては)確定する ○ □□ i □□■ ×: DP1[i] から壁マスへの遷移を計算した後、 □□×× DP2[j] が確定する範囲(壁マス同士を除いて) □□××■ □□××□□ →×の中で左上の壁マスから順次確定させていける □□××□□■
「×内の壁マス→それより右下の壁マス・角マスの遷移」を左上から計算し、DPに足し込んでおく。
その後、分割統治FFTの続きを再開すれば、正しいDPの値となる。
壁マスにまつわる遷移は、M 個それぞれで、計 O(N+M) 個の他の黒マス {から/へ} の遷移を計算するため、O(M(N+M)) かかる。
あわせて、O(Nlog2N+M(N+M)) となる。
PythonだとTLEがやや厳しいが、畳み込み部分だけでもNumbaで高速化したりすると通る。