目次

AtCoder Beginner Contest 196 D,E,F問題メモ

AtCoder Beginner Contest 196

D - Hanjo

D - Hanjo

問題

解法

「畳」と言ったときは $1 \times 2$ の畳を指すものとする。

制約から敷き詰め方を全探索するのだろうとは思うが、実装の仕方にちょっと迷う。

$A$ 枚の畳を敷き詰めると、 余ったスペースへの半畳の敷き詰め方は勝手に決まるので、 半畳は無視して畳の置き方をDFSなどで探索すればよい。

既に各マスに畳が置かれた・置かれていないだけが重要で、畳の種類や縦横などは関係ないので、$2^{HW}$ の情報量で状態を管理できる。

置き方の表現方法としては、$H \times W$ の二次元配列でもよいが、 以下のようにすると1つの数字で表現できる。

0-indexでの $i$ 行 $j$ 列目を $(i,j)$ として、$i \times W + j$ とすることで 各マスを $0~HW-1$ の連番に対応させられるので、 これのbit集合で「畳を置いたマス」を表現する。

<>.      012      876543210
^..  ↔  345  →  001001011  =  75
v..      678

Python3

E - Filters

E - Filters

問題

解法

たとえば $\infty,0,-\infty$ などから始めたときにどうなるかを考えると、

 ∞  ------
  :       |
 20       |      ,------------,
  :       |    ,'              `,
 10       ----'  ,------------,  `----  ----
  :            ,'              `,        ↑
  0  ---------'                  `----  答えの
  :                                    取り得る
-10                     ------,          範囲
  :                     |      `,        ↓
-20                     |        `----  ----
  :                     |
-∞  -------------------
       min(10)  +10  max(-10)  -10

最初に $\min(10)$ が来たら、$10~ \infty$ の数は全て $10$ になり、以降の挙動は同じとなる。

以下を管理していけば、

$x_i+D$ が上限・下限を超えてしまうような $x_i$ の場合は、上限or下限。 超えていなければ答えは $x_i+D$。

Python3

F - Substring 2

F - Substring 2

問題

解法

フーリエ変換によるたたみ込み。知っていることが前提になると思われる。

たたみ込みは、原理は何やってるか理解が難しいが、とりあえず以下のことができるアルゴリズム。

愚直には $O(NM)$ かかりそうな所、$L=N+M$ として、$O(L \log{L})$ で行える。すごい。

C++ なら、ACLに既存の実装があるので自前で実装せずとも利用できる。
Pythonなら、オーバーフローするものは無理だが、今回のように答えが64bitに収まることがわかっている場合は numpy.fft などを利用できる。

さてこれを今回の問題にどう使うか。

$T$ を $S$ のどの位置に一致させるか決めたら、0か1しか出てこないので、各桁ずつのXORで1となった個数がそのまま答えとなる。

S    10101000010011011110
T        0010011111
XOR      1010001100       合計 4

S    10101000010011011110
T           0010011111
XOR         0000000100    合計 1

$S$ の全ての開始位置について求め、その中の最小を取れば答え。

ここで、$T$ の各文字に逆からindexを振ってみる。

i    01234567890123456789
S    10101000010011011110
T        0010011111
j        9876543210

すると、合わせる位置が同じ $|T|$ 個のペアは $i+j$ が全て等しくなる。

ここにたたみ込みを利用できる。
$(i,j)=(4,9),(5,8),...,(13,0)$ のそれぞれで $S_i \oplus T_j$ を計算して合計した値は、まさしく $S$ の $4$ 文字目を開始位置としたときの答えとなる。

ただし、高速なたたみ込みができるのはかけ算だが、今必要な演算はXORなので、何らかの変換が必要になる。

「$0,1$ 間のXOR」を「$1,-1$ 間のかけ算」に変換してやると、結果が見事に対応するので、これを利用する。

たたみ込みで求められる値 $c_k$ は $|T|$ 個の $-1,1$ の和で、今回はこの中で $-1$ の個数を求めたい。
よって、$\dfrac{|T|-c_k}{2}$ が $-1$ の個数、ひいては元のXORの演算での1の個数となる。

$c_k$ の先頭と末尾の各 $|T|-1$ 項は、$T$ が $S$ からはみだすような置き方となってしまうため、除いて考える。

Python3

暫定コストの小さい方から経路探索的に1文字ずつ合わせていく0-1BFSで、1ケース以外は全部通ったんだけど、まぁダメだったよね。 反例も比較的簡単に作れちゃうし。