O は解法は正しかったけど定数倍に敗北して 10 分間に合わず. ただ, 直接の敗因は間違いなく SASUKE が面白かったせい (え?)
A - ○✕ゲーム
S.contains("ooo")
, S.contains("xxx")
をそれぞれ見る (同時に成立することはない).
B - 上書き
ArrayList<Character>
で処理中の文字列 t
を持っておくと, 文字 c
の削除は t.removeIf(e -> e.equals('c'))
で判定できる. 追加は t.add(c)
.
確かめてないけど, removeIf
の中の判定を e == 'c'
でやると壊れると思う. Integer
みたいに ASCII 文字の範囲がキャッシュされてたら大丈夫だけど, その辺は把握してない.
C - 三十六進法
問題文から 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ
をコピーして, charAt
なりで求めるのが楽そう.
D - リーディングゼロ
まず, 先頭に 0
がない場合を考えると, と の数としての順序と文字列としての順序は次のように対応づけられる.
- の場合: 数として
- の場合: 文字列としての順序と数としての順序は一致する
- の場合: 数として
なので, こういう風にソートすれば OK.
Arrays.sort(s, (x, y) -> { int l = x.length, m = y.length; int i = 0, j = 0; while (i < l && x[i] == '0') i++; while (j < m && y[j] == '0') j++; int rx = l - i; // x から先頭の 0 を除いたときの長さ int ry = m - j; // y から先頭の 0 を除いたときの長さ if (rx == ry) { // ケース 2 int cmp = Arrays.compare(x, i, l, y, j, m); if (cmp == 0) { // この問題では, 0 の数が多い方が小さいとみなす return j - i; } else { // 文字列としての比較 return cmp; } } else { // ケース 1, 3 return rx - ry; } });
E - ハンコ
やばすぎ. 解法としては, 4 パターンの回転と, 座標の重なり方を全探索するだけなんだけど...
汚くなったので省略.
F - 一触即発
一転して安心感のある全探索. 通りに対して「物質に薬品 を新たに追加すると爆発するような薬品 の数」を数えるだけ. 全体 で書くと安心. だと単純計算で くらいになって危なそう ?
G - ヘビ
要するにハミルトンパスを見つけて復元する問題. dp テーブルを以下で定義すれば OK (日本語がやばい...).
H - コンベア
ゴールから逆方向に BFS するだけ.
I - 避難計画
避難所から逆方向に多始点 BFS するだけ. 同じ問題が連続して並ぶのはいいのか... ?
J - 長い長い文字列
再帰. コード見た方が早そう.
static char solve(char[] s, long x) { long c = 0; for (char t : s) { if ('a' <= t && t <= 'z') { c++; if (c == x) return t; } else { int rep = t - '0'; // 繰返し回数 if (c * (rep + 1) >= x) { // 基本的には x % c 番目を求めたい // しかし, 丁度割り切れる場合に壊れるので (x - 1) % c + 1 とする return solve(s, (x - 1) % c + 1); } c = c * (rep + 1); } } throw new AssertionError(); }
K - 的あて
難しかった. bit DP をすればよくて, テーブルは以下で定義する.
遷移を考える. 的 に当たった場合は, の値を参照すれば良いが, 的に当たらなかった場合は自身を再帰的に参照することになるので, 式変形が必要.
いま, に対して最適な目標マス が分かっていると仮定する. このとき, 当たる可能性のある "マス" の集合 と当たる可能性のある "的" の集合 が定まる. ここで, もまた成立し, 以下の式が成立する.
両辺の をまとめて, 以下の式を得る.
の値は事前には分からないので実際には以下を計算すればよい.
L - T消し
難しい. 結論から言えば, 区間 DP を 2 回やると解ける. 以下では, は半開区間 を表すものとする.
一回目の区間 DP では, 次のテーブルを埋める.
のときは, . のときは, 区切り位置 を全探索して, 以下のいずれか成り立つかを見ればよい.
つまり (?),
二回目の区間 DP では, 次のテーブルを埋める.
これは以下のように求められ, 答えは である.
計算量は全体で .
M - 棒の出荷
決め打ち二分探索をしたくなって, 実際にやってみると解ける. つまり, 長さの下限 に対して, 次の条件 を満たすような分割が存在するかを判定する.
この判定問題は, 次で表される集合 を の昇順に求め, かどうかを見ることで解ける.
は,
を前計算しておくと以下のようにして求められる.
ここで, とした. これらの計算は累積和や二分探索, 平衡二分探索木 (または Segment Tree) を用いることで高速に行える.
N - 旅行会社
セグ木上で二分探索をする.
O - 通知
クエリ平方分割.
個毎に全ての通知を伝播することにすると, 伝播は 回起こり, 毎回 時間かかるので伝播パートは全体 となる.
クエリに答えるパートでは, 個しか必要な頂点が存在しないので, 不要な頂点を消したグラフを用いることで愚直にやっても毎回 時間で答えることが出来る. クエリは合計 回飛んでくるので, クエリに答えるパートは全体 時間となる. グラフの再構築にかかる時間は全体で である.
以上より, 全体 時間でこの問題を解くことが出来る. ただ, 定数倍が結構きつくて, 隣接リストを実際にリストで持つと TLE した. 配列に直すと定数倍が改善されて 2000 ms を切った.