目次

AtCoder Beginner Contest 242 F,G問題メモ

AtCoder Beginner Contest 242

最近のABCのCとかD、普通にむずいんだよな

F - Black and White Rooks

F - Black and White Rooks

問題

解法

まず、1つの行や列に、黒と白両方が置かれることはない。

「●・・●・・・○」みたいな配置を考えたとき、 混在していると必ず黒と白の境界が存在するので、その部分の駒が互いの利きに入ってしまう。

なので、行と列をそれぞれ、以下のグループに分ける。

「黒のみ置かれる行数 $R_b$」「黒のみ置かれる列数 $C_b$」「白のみ置かれる行数 $R_w$」「白のみ置かれる列数 $C_w$」として これらを決め打ったとき、その行・列を $N \times M$ のどこに配置するかは、以下のようになる。

また、以下がわかれば、

全体の置き方の個数は、これらの積となる。

$f_b,f_w$ をDPで求める

$R_b,C_b,R_w,C_w$ の全探索は $O(N^2M^2)$ で可能なので、あとは $f_b,f_w$ を前計算などで $O(1)$ で得られればよい。

重複を避けるため、$f_b,f_w$ は必ず空行・空列のない置き方で無いといけない。

B=3 Rb=3 Cb=2
OK      NG      NG      1つめのNGの置き方は、
●・・  ●・・  ●●●  Rb=2 Cb=2 を探索したときに既に数えられている
・●●  ●●・  ・・・

重複を気にしなければ、$R_b \times C_b$ 個のマスの中から置く $B$ マスを選べば良いので、${}_{R_b \times C_b}C_{B}$。

そこから空になってしまう行数・列数を $R_x,C_x$ と決め打てば、除外すべき置き方はどの行・列を空にするかを含めて、以下のようになる。

これを $0 \le R_x \le R_b, 0 \le C_x \le C_b$(ただし $R_x=R_b$ かつ $C_x=C_b$ を除く)の範囲で全探索し、 ${}_{R_b \times C_b}C_{B}$ から除外すれば、$f_b(R_b,C_b)$ が求められる。
これも $O(N^2M^2)$ で計算できる。

Python3

包除原理で求める

何の個数を $(-1)^k$ の $k$ とすればよいか少し迷うが、「必ず空行・空列にすると定める行数+列数」とできる。

空行があってもよい条件でそれぞれの場合を求め、

その上で $(N+M-R_b-C_b-R_w-C_w)$ の偶奇によって答えに足す正負を切り替えると、最終的な答えが求まる。

これなら前計算は必要なく、$R_b,C_b,R_w,C_w$ の総当たりのみで済む。

相変わらず、包除原理はなかなか直感的には見えづらい。

G - Range Pairing Query

G - Range Pairing Query

なんでこんな解かれてるの?

問題

解法

TLE解法

SegmentTreeなどでは解きづらい。
1種類の値 $x$ に限定すれば、SegmentTreeを使って区間 $[l,r]$ に含まれる $x$ の個数を取得し、 2で割って切り捨てることで $x$ で作れるペア数は $O(\log{N})$ でわかるが、 それを$A_i$ の値の種類数… $O(N)$ 回繰り返さなければならない。全部で $O(QN\log{N})$ で余裕のTLE。

1つの値を1bitで管理してXORでマージすれば、「ペアにできない $x$ が存在するかどうか」がわかり、 popcountして区間長から引くことでペア数もわかるが、それもbit長が $O(N)$ になり、XORの演算に時間がかかるため無理。

公式解説の解法

Mo's Algorithm、クエリ平方分割を使う。なんか名前だけ聞いたことあった。

以下のような場合に使える。

今回の場合、現在の区間内の各値の個数(の偶奇)を管理しておけば、以下の要領で区間を1つ伸縮したときの答えを更新できる。

何らかの順序でクエリを並べ替え、その順に区間を伸縮させて答えを求めていくことを考える。

例えば $r$ の昇順でクエリをソートし、この順に区間を伸縮させて答えを求めていくと、
右端の伸縮は最小限となるが、左端の伸縮で毎回 $O(N)$ かかってしまう場合がある。

1              |-|
2 |----------------|
3                 |-|
4 |-------------------|

そこで、$l$ を、なんか適当なブロックサイズ $B$ でグループ分けする。

その中ごとに $r$ の昇順でクエリを処理すると、左端の伸縮も1クエリ当たり最大 $O(B)$ で収まる。

  <-B->
1     |-|
2 |---------|
3     |--------|
4 |-----------------|

分割した分、この処理をグループの個数 $\dfrac{N}{B}$ 回繰り返す必要があるが、

全体の計算量はこの和となるので、$B=\dfrac{N}{\sqrt{Q}}$ 程度に設定すれば $O(N\sqrt{Q})$ となる。

本問題の制約では最大 $10^8$ となるが、、、 まぁ $r$ を動かす範囲は右のグループほど狭まるなどの定数倍の削減があり、間に合う。

Python3

オンラインの場合

クエリが先読みできないときも、別の方法で高速化は可能。