# 5/10 競プロ講習会Cクラスバチャ解説 ## ARC025-B チョコレート ### 問題概要 - $H \times W$マスのチョコレートがある - 市松模様に白黒に塗られている - 各マスには数字が書かれている - 白のマスの数字の合計と、黒のマスの数字の合計が等しくなるように長方形を切り出すとき、マス目の個数の最大値は? - $H,W \le 100$ ### 所感 - 二次元累積和の練習問題 (逆に言えば知らないと無理かも) - $A=B$ を $A-B=0$ として扱う典型テクも含まれている - 下手したらABC-Cくらいでも普通に出てきそう - ぜひ通しておきましょう!! ### 解説 - まず、問題の条件を簡単にしたい → 白と黒の合計値が同じを、簡単に言い換えられないか? → 黒の箇所に -1 を掛けてみる → すると「長方形領域の総和が0」であることが、白黒の合計が等しいことと同値になる - あとは、「総和が0」の長方形領域のうち最大サイズのものを求める問題になる - まず基本の全探索を考えてみよう → 探索すべき長方形の個数は左上と右下の箇所の組み合わせなので O($H^2 W^2$) → 長方形のサイズは O($HW$) → 合わせると O($H^3 W^3$) で、ざっくり $10^{12}$回くらい → 厳しそう(実際は $3 \times 10^{10}$回くらい) - たくさんの長方形領域の和を高速に計算する方法がある!! → 二次元累積和 → 具体的には前準備 $O(HW)$, クエリ $O(1)$ → これを使うと、長方形内の和を計算するフェーズが高速化されて $O(H^2W^2)$になる - 二次元累積和に関して → 検索するといっぱいあるので、調べてほしい → まず、 1-indexed で2次元配列に格納する \ 例: 0000 0132 0101 \ → 縦に累積和する 0000 0132 0233 \ → 横に累積和する 0000 0146 0258 →ここまでで前準備完了!! \ 右下4マスの総和を知りたいときは・・・ 0 **0** 0 **0** 0 1 4 6 0 **2** 5 **8** \ に注目して・・・ $右下-右上-左下+左上=8-0-2+0=6$ と計算すればOK - 二次元累積和アルゴリズムの理解に関して 1. 前処理後の各マスが、どの値の和になっているかを考えると理解できそう 2. クエリの処理に関しては、包除原理に基づいている点を意識 --- ## ABC001-C Shorten Diameter ### 問題概要 - $N(\le 2000)$ 頂点の木がある - 葉を削除していく - 直径を $K$ 以下にするために必要な削除数の最小は? ### 所感 - 600点にしてはかなり簡単 - ABC-Dで出ても不思議ではない? - やることさえ分かればDFS,BFSの練習問題 - 木の直径に関する知識・素養は必要かも - ぜひ通しておきたい!! ### 木の直径に関して - 木の直径の求め方 (今回の本質ではない) 1. なんでも良いので点$a$を持ってくる 2. 点$a$からDFSを行い、一番遠い点 $b$ を見つける 3. $b$ からDFSを行い、一番遠い点 $c$ を見つける - 証明はネットを見てください… - 直径は複数存在しうるが、直径の中心は1つしか存在しない(今回重要な定理) - 証明: 直径中心が2つある場合、直径が伸びてしまう ### 解説 - 直径の中心を全探索することを考える - 中心を決め打つと、そこからの距離が $K/2$ より大きい頂点を全部捨てればOK! - 全ての辺の長さが1なので、直径の中心は以下の2種類しかありえない 1. 頂点 2. 辺の中間点 - よって、探索すべき中心の個数は $N+(N-1)$個 - 各探索は $O(N)$ なので、全体で $O(N^2)$ ### 実装に関して - 辺の中間点を中心にするとき、DFSが書きづらい・・・ - 辺の中間点にも頂点を足してあげると実装しやすいかも - 個数を数えるとき、中間点を数えないよう注意!! - 辺の長さが全部 0.5 になることに注意!! --- ## Mujin Programming Challenge 2017-B Row to Column ### 問題概要 - $N \times N (\le 500)$ のマス目 - 各マスは白黒どちらかに塗られている - 以下の操作を繰り返して、全部黒にできるか? 可能なら最小回数は? - 好きな行をコピーして、好きな列にペーストする ### 所感 - まず、配点に面食らうこと間違いなし - 出題された場合、問題を読むことすらなく終わってしまう人が多数なのでは? - 高配点なのに、特殊な知識などは一切要求されない - じゃあAGC特有の超天才ひらめき問題! と見せかけて、理詰めで解ける - 配点に惑わされるな!! ### 解説 - まず、全部白の場合当然不可能 - 一方で、黒があると必ず可能 - 手を動かしてみると、絶対可能そうだな、という気分になる - 回数を求めるに際して、最後の操作を考えてみよう - 最後の操作の直前、コピー元の行は当然全部黒 - すなわち、全部黒の行を1つ作るのは必須 - もう少し考えると、黒の行は早めに作る必要があることがわかる - 全黒の行以外をペーストした列は、後に全黒をペーストする必要がある - 全黒行を作る → 全黒ではない全ての列にペースト が必要な流れであることがわかる - ここまでを踏まえた素朴な解答方針案 1. 全黒の行を1つ、最速で作成 (このときに全黒列の個数が変わると面倒だが…?) 2. 全黒ではない列全てにペーストする - 上の解答方針に関する考察 - 1. において全黒列の数は減らない(既に全黒列の箇所を上書きするメリットがないため) - 1. において全黒列の数は増えない(コピー元が全黒なわけがないので) - つまり、全黒行を最速で1つ作成する際に、全黒列の個数は変わらない - 以上のことから、2. の手数は最初の状態における、全黒ではない列の個数に等しい - あとは、全黒行の最速作成方法を見つければOK!! - 全探索を考える。 ( $i$行目は最短何手で全黒に? ) - $(j,i)$ が黒のとき、$j$ 行目をコピーして貼り付けまくればいい - そうでないとき、$i$列目に黒を持ってくるために追加で1手必要 --- ## 第一回日本最強プログラマー学生選手権決勝-A Equal Weight ### 問題概要 - $N (\le 2 \times 10^5)$ 個のシャリと、$M (\le 2 \times 10^5)$ 個のネタがある。同じ重さのスシを2個作ってください。 - シャリ、ネタの重さは $10^6$ 以下(重要) - 同じ重さのシャリ・ネタは存在しない (優しい制約) ### 所感 - 300点とは思えない難問 - 多くのredcoderが本番落としていることからも、気が付かないと永久に解けないタイプの問題 - 点数に騙されるな!! & 制約はちゃんと読もう!! - かなり教育的な問題ではあると思う ### 解説 - 作れるスシの重さは $2 \times 10^6$ 通りしか無い - 適当にスシを作っていると、比較的早く衝突する - 同じ重さのシャリ・ネタがないため、衝突したならば、2つのスシの材料が異なる - 終わりです ### 実装方針 - mapやdictに作ったスシの重さをkey、材料valueとして保存していく --- ## AGC009-B Tournament ### 問題概要 - $N$ 人いて、1 vs 1の対戦をバラバラにする - 負けた人は即退場 - 優勝者(No.1)以外について、誰に負けて退場したかの情報が与えられる - トーナメント表の深さとして考えられる最小値は? ### 所感 - ABC-E,Fで出てきても違和感無いと思う - 私が初めて解いた 700点以上の問題(だったはず) - 典型よりなので解いておくことをおすすめします!! ### 解説 - 「部分優勝トーナメント表」という概念を導入しよう - 人$v$に関する部分優勝トーナメント表は、$v$が勝ち抜いていて、なおかつ次の対戦で $v$ は負けなければならないようなトーナメント表 - 各人$v$に関して、その人の部分優勝トーナメント表の深さの最小値 ($dp_v$) を求めていけば良い - 人$v$が、 $v_1,v_2,...,v_k$ の $k$ 人を倒さなくてはいけないとき… - 当然、$dp_{v_i}$ が小さい方から貪欲に戦った方が良い - あとは、木dpで解ける --- ## AGC040-C Neither AB nor BA ### 問題概要 - `A,B,C` のみからなる長さ$N (\le 10^7)$の文字列のうち、以下の条件を満たす個数は? - 連続した2文字を消す。ただし、`AB,BA` は禁止 - $N$ は偶数 ### 所感 - 難しい!! - 高度なひらめきが要求される - ただ、本問題のメインアイデアは他の問題でも見るため、典型でもあると思う - 解いておくと後々生きるかも? ### 解説 - 問題を簡単にする重要なメインアイデア - 偶数文字目の `A`と`B` を反転する - すると、 `AA,BB` が削除禁止になる - AA,BBが削除禁止のとき、なるべく多く消す最適方針は? - `AB,BA` > `CA,CB,AC,BC` > `CC` の優先度で消す - 消すのが不可能な場合を考えてみる - `A` か `B` が過半数だったら、自明に不可能 - そうでないとき、`A`,`B` のうちで多い方を消す手段があり、実行した際に `A`,`B` 共に過半数にはならない (以下雑な証明) 1. 証明: `A`が`B` 以上の個数のとき、`AB,AC,BA,CA` を消して`A`が過半数になることはない - ギリギリの場合 = `A`がちょうど半数 を考える - `A` , `A以外` が1ずつ減るので、操作後も `A` は半数となる - ギリギリの場合でOKなので十分 2. 証明: `A`が`B` 以上の個数のとき、`AB,BA` を消して`B` が過半数になることはない - 操作後の `B` の個数は `A` 以下なので自明 3. 証明: `A`が`B` 以上の個数のとき、`AC,CA` を消して`B` が過半数になることはない - ギリギリの場合 = `B` がちょうど半数 → `A`の個数は`B`以上だから、そもそも `C` が無い - `B`が半数未満の場合、文字列が2縮んでも過半数にはなりえない - 証明できたので、あとは数え上げ - `A` が過半数になる場合 が分かればOK - `A` の個数を全探索すれば、`A`の位置を決め、あとは残りの位置に `B,C` を好きに入れればいい - $O(N)$