−目次
AtCoder Beginner Contest 211 D,E,F問題メモ
夏の競プロは蚊が天敵
D - Number of Shortest paths
問題
- NN 頂点 MM 辺のグラフが与えられる
- 単純であることは保証されるが連結であるとは限らない
- 辺のコストは全て1として、頂点 11 から NN への最短経路は何通りあるか求めよ
- 2≤N≤2×1052≤N≤2×105
- 0≤M≤2×1050≤M≤2×105
解法
DijkstraやBFSなどで最短距離を求めるとき、各頂点への「最短距離」の配列を管理するが、 最短経路のパターン数を求める場合は、「パターン数」も一緒に管理する。
- dist[v]: 頂点 vv への(暫定)最短距離
- pat[v]: 頂点 vv への距離が dist[v]dist[v] であるような経路のパターン数
queueから取り出した頂点 vv(最短距離が確定した頂点)から、隣接する頂点 uu へ伸ばすときを考える。
vv を経由して uu に至る距離を d=dist[v]+1d=dist[v]+1 として、
- 既に uu への最短距離で、dd より短いものが見つかっている場合(dist[u]<ddist[u]<d)
- 何もしない
- 既に uu への最短距離で、dd と同じものが見つかっている場合(dist[u]=ddist[u]=d)
- pat[u]+=pat[v]pat[u]+=pat[v]
- まだ uu が未探索、または dd より長いものしか見つかっていない場合(dist[u]>ddist[u]>d)
- pat[u]=pat[v]pat[u]=pat[v]
- queueに uu を追加
とすれば、通常の最短経路探索と比較して無駄に探索範囲を広げることなく、パターン数も求められる。
今回は辺のコストが全て1(正)なので、queueから uu が取り出される前には dist[u]dist[u] も pat[u]pat[u] も全て網羅されていることが保証される。
ただし、辺のコストに 00 があったりすると、 uu にコスト0の辺を使って到達できる経路がまだある状態(pat[u]pat[u] が確定していない状態)で uu がqueueから取り出されてしまうことがあり得る。
その場合は無向辺ならパターン数は無限大になるので問題の体を為さなくなるし、 有向辺ならコスト0の辺だけで最初にトポロジカルソートなりして、 距離が同率の場合はソート順にqueueから取り出せばいいのかな?
E - Red Polyomino
問題
- N×NN×N のマス目が与えられ、マス (i,j)(i,j) は Si,jSi,j が'#'なら黒く、'.'なら白く塗られている
- 白く塗られたマスを KK 個選んで赤く塗る
- この時、赤く塗られた KK マスが全てタテヨコにひとつながりになっているパターン数を求めよ
- 1≤N≤81≤N≤8
- 1≤K≤81≤K≤8
解法
N,KN,K が小さいのである程度全探索的なことを考える。
まぁ、全探索とはいえ N2N2 マスから KK マス選ぶ方法を全探索して連結が調べる、なんてのはさすがに無理。
サンプル3が優しくて、パターン数が最大である N=8,K=8,N=8,K=8, 全マス白マスのケースが64678しかないことを教えてくれている。
なので、「赤く塗ることを仮定するマスは常に連結」となるように探索範囲を広げていけば、そこまで状態数は大きくならなさそう。 マスの数も64個で、丁度64bit整数に収まるので、 bitフラグで赤く塗ることを仮定した頂点を管理することにする。
N=4 0 1 2 3 4,8,9を赤く塗った状態を表す整数 4 5 6 7 → 5432109876543210 8 91011 0b0000001100010000 = 784 12131415
探索済みの状態の集合 checked をsetで管理しておく。
まず、白マスを1マスだけ塗った状態を全てqueueに突っ込む。
queueから取り出した状態につき、
- その状態で赤く塗られているマスを復元する
- 上下左右に隣接している白マスから新しく1マスを赤く塗った状態に遷移する
- 遷移後の状態が既に checked ならスキップ
- そうでなく、KK マス塗られた状態なら、答えに+1カウント、checked に追加
- そうでないなら、queueとcheckedに追加
としていけば、求められる。
F - Rectilinear Polygons
問題
- 二次元平面上に NN 個の多角形がある
- ii 個目の多角形は MiMi 角形で、各頂点の座標も与えられる
- 多角形は単純(連続しない2辺が公差や接触していない)
- 全ての多角形の辺は、xx 軸または yy 軸に平行
- 頂点の座標は順に与えられるが、時計回りか反時計回りかは不明
- 以下の QQ 個のクエリに答えよ
- 座標 (Xi+0.5,Yi+0.5)(Xi+0.5,Yi+0.5) には、いくつの多角形が重なっているか?
- 1≤N≤1051≤N≤105
- 全多角形の頂点数の合計 ≤4×105≤4×105
- 0≤0≤ 多角形やクエリの x,yx,y 座標の値 ≤105≤105
解法
クエリ先読みでイベントソート。
「多角形のうち垂直な辺」「クエリ」を全てまとめて xx 座標でソートする。
1つの多角形の1つの垂直な線が Y1~Y2 まで引かれていたとして、 ここを境として左と右では、Y1~Y2 に対するクエリの答えが+1または-1される。
x 座標の小さい方からFenwick Treeなどで管理してやれば、
- 多角形: 区間 Y1~Y2 に1または-1を加算する
- クエリ: 現時点の Y の値を求める
という区間加算の問題に変わり、各操作を O(log(座標最大値)) で処理できる。
さて問題は、各多角形の垂直な線が+1(外から内)なのか-1(内から外)なのかをどうやって判定するか。
これは、座標の与えられ方が時計回りか反時計回りかに依存する。
- 時計回りなら、与えられた順で↑向きの辺で+1され、↓向きの辺で-1される
- 反時計回りなら、↓向きの辺で+1され、↑向きの辺で-1される
では、時計回りか反時計回りかをどう判定するか。
最初の3点が右に折れ曲がっていたからといって、時計回りとは限らない。
④←←③ ↓ ↑ ⓪→①→②だけを見ると右に折れ曲がっているが、 ↓①→② 座標の与えられ方は反時計回り ↓↑ ↓⓪←⑦ ↓ ↑ ⑤→→⑥
多角形の座標を全て (x,y) でソートすれば、一番左下にある垂直な辺を取り出せる。(図では④→⑤)
この辺は一番左下なので、(当然だが)もっと左を回り込むような辺は無い。
なので、必ず外→内の辺である。
これが上向きなら時計回り、下向きなら反時計回りだといえる。

