PAST202107-OPEN

バチャやってHM以外を解いた。前半重いのは相変わらずだけど、そこまで重いのはなかったし、典型だけど天才っぽさを感じられるのがあったりで、好みのセットだった。

A - チェックディジット/check digit

言われた通りに実装する。15文字目は足さないように注意。

B - 蒸気圧/Vapor Pressure

シミュレーション。ストーリーの意味を考えると混乱するので、問題文の通りに実装するだけ(あまり好みではないが)。

C - 入力チェック / Validation

余分な0の有無は先頭が0かどうかで判定できる(と思ったらサンプル通らず0が例外だったまたお前か)。あとは10^100くらいになる数との大小関係だが、上から10倍しては足ししていきRより大きくなればNoで、最後まで行ったら普通に判定する。

(解説を見て)不要な0の判定が済んだら、単に文字列の長さでいいのね。不要な0がある場合はL以上R以下の判定が不要というの、なかなか気づきづらい。

D - 書き換え/Replace

難しそうな見た目だが、aiueoに気づいて一安心。同じ文字を取り合うことがないので、左から貪欲に取って損しない(当時はここまではっきりとは考えられなかったが)。aiueoかの判定は p["aiueo"[i]] = 1; というテーブルを作っておけばよい。そしてxを全探索。

E - 青木君のいたずら/Aoki's Trick

愚直に全探索。二重ループになるのでちょっとややこしいか。一重ループで書きたいところだけどね。

F - ダブルブッキング / Double Booking

ミーティングは全て1時間単位なので、日毎に何時が埋まってるかをビットで持つ。

ここまで、理不尽に重い問題がなくて快適。

G - べき表現/Power Expression

いよいよ力を試される問題が出てきた。緊張して便意。Gはほぼ解けていたので、後ろのほうからすぐ読めるものを探しNを頭に入れてからトイレへ。

天秤で量るとき3冪の分銅があればいいみたいな話は有名だが、それについて考えてなかったなと思い焦る。下から合わせていくのを思いついて解けた。3進法で最下位は1で合わすしかないので、0なら使わず、1なら1を、2なら-1を使う(それ以外の選択ではNを作れない)。引いて3で割ってまた同じことをする。

H - 折れ線グラフ/Line Chart

なにこれ。どうせ簡単な貪欲だろと思ったら何もわからない。また0に戻ってくるのが厄介だよね。上に凸になる気はするけど、どんなケースでもいけるこれという方法は浮かばない。後ろのほうを解いてから戻ってくると、DPを思いつく。Aは和だけが重要。しかし和が10000近くまで行くので計算量がやばい。ギリギリ行けそうな気もするが、ここでそんなのやらせるか?必要な探索はそこまで多くなさそうだけど、簡単に厳密さを求めるとやっぱりデカくなる。隣との高さの差でコストが決まる。幾何なのか幾何無関係DPなのか全然わからない。

(追記)解説覗いて計算量見て気づいたけど、総和が100以下という制約見落としてた(????)。幾何無関係DPだった。ただのDPだから書けるけど、実装はかなり時間がかかった。やはりちょっとでも慣れない要素が入ると自分の中のDP苦手な面が出てくる。

I - ほくろ/Nevus

落ち着いて考えないとだめなやつ。まず、目と目の中点が原点に来るように平行移動。次に左目の角度をatan2で求めてそれが0度になるように回転。結局は簡単だが、この方針に到達するまでにかなりの時間を要した。最初は右目を基準にしようとしてたし、Eも求めちゃったしね。実行時間が予想より長かったけど、fixed << setprecision(9) を毎回付けてるの遅いんか。

(追記)C++の標準機能で出力するとどうやっても遅かった。えー知らなかった。解説見て、atan2なしで解けることに気づいた(この回転最近やってなかったな)。

(追記)doubleの出力を自前で書きFastestを取った。

J - 終わりなき旅/Never Ending Journey

有向グラフの閉路検出。苦手。ACLのSCC使うことも考えたくらい。DFSして祖先ノードに到達すれば閉路ある、そうでなければ閉路ない(ないことの証明ができない)。全頂点からDFSする予定だったのに頂点0からしかしてなくて1WA。

K - 急ぎ旅/Flying Trip

最短経路だけならダイクストラ。少し考えて、満足度も組み込めるのでダイクストラで行ける。景観が辺ではなく頂点にあるのが厄介だが、その頂点に向かう辺のコストにしてしまえばいい(サンプルが合わなかったが、満足度の計算でスタート地点が入ってなかった)。ダイクストラ法のコスト計算で、通常の距離が同じなら満足度が高いほうがよい(コストが小さい)とする。これだけだが、ライブラリはそこまでは抽象化してないので書き換えに手間がかかった。priority_queueで使うため比較演算子を逆にしてあるのでそこも大変だった。ラスト6問、このへんからACが嬉しくなる。

L - たくさんの最小値/Multiple Min Query

unorderd_mapで各値に対しsetを持つという地獄を思いついたが、なんとかセグ木だけで解けてよかった。jの列挙が要らないならセグ木で簡単。そこで、セグ木に少し実装を足して対応した。遅延セグ木の再帰コードを持ってきて、セグ木の一番上からクエリの区間を含みかつ最小値がpのところを掘る。一番下に到達したらその位置自体が答えなのでvectorに突っ込んでいく。このコードがなかなか動かず時間を食った。まず、最小値がpのところだけを見るのではだめで、p以下のところを見ないといけない(区間外のより小さい値があるかも)。そして、最小値を関数に渡したとき再帰側では渡し忘れていた(デフォルト引数を使っていたのでコンパイルエラーにならなかった)。

(解説を見て)あー!最小のjを得るセグ木、典型だった。まあ自分なりの良い方法で解けたのでよし。

(追記)上で書いたunordered_map<int, set<int>>とノーマルセグ木で275msだった。地獄どころか普通に実用レベル。実装もそんなに時間かからなかった。

M - 分割/Divide

ラスト3問は難しい。ここでぱったりとACが止まる。並列に考えていく。

誤読して簡単じゃんとDPを書くが、サンプルでより大きい答えになる。部分列だから連続するとはかぎらなかった。色々なDPを考えるが、まとめて扱えるものが見つからなかった。

(追記)数日間苦しんだ。解説の通りに辺を張って(意味がわかってないので間違えないように張るだけでも大変だった)サンプルが通ることを確認し、そのコードを元に図を描きイメージをつかもうとする。全然わからない。部分列としてではなく、誰かのお尻に付けてもらうことでコストCを免れるという捉え方に気づいて進展した。N個の頂点を2セット用意し、左右2列に並べる。Sから左へコスト0で1ずつ流す。右へはコストCを払えば最初から行ける。Cを払うのが嫌なら差の絶対値を払って自分より後の番号へ左から右へ行ける(左へ入るのも右から出るのも1なので分岐や合流のない列になる)。右からTへ1ずつ流し、流量はN。部分列に沿って流すのではなく、各A_iについて自分が先頭になるかそうでないなら誰のお尻に付くかを選ぶ感じ。

N - モノクロデザイン/Monochrome Design

トイレで考えていたときは何もわからなかった。座圧からのいもすすらできない制約だ。落ち着いて考えると平面走査ができそうだ。左から右に見ていく。Y座標は座標圧縮して圧縮後の1マスの長さを別に保存しておく。クエリは長方形を左右2つの線分に分ける(左右どっち由来かは区別する必要がない)。遅延セグ木は区間和と、和に含めるかどうかを区間反転。さっき保存しておいた長さはここで使う。反転は区間全体の長さから現在の和を引くだけ。実装に時間がかかるし、遅延セグ木で本当に表現できるのかも最後まで確信が持てないしで、大変だった。

O - コンピュータ/Computer

かなり難しい。とりあえずBは広義単調増加になるよう変更していい。後ろから考えたくなり、しばらく考えてそれを元に前から見ていく2乗のDPを書くが計算量を落とせる気がせず一旦諦める。しばらくして、貰うのではなく配れば連続した区間で扱えそうなことに気づく。実装にめちゃくちゃ時間がかかった。買うコンピュータはB_iに出現する価格に限定してよく、ギリギリになってから(場合によっては未来を見据えて高いものを)買うとしてよい。遅延セグ木では、今日のために過去(今朝を含む)買ってもらったときの今日の所持金の最大値を管理する。未来のためにコンピュータを買うときは、今日のためのコンピュータを持っていないときの所持金の最大値を持っておき、お金が足りるならば何日後のぶんまで買えるか二分探索で求め、買える区間に対しmaxをかける。遅延セグ木も不安だったが、最終的には区間max(変更するほう)だけでよくなった。具体的には、収入の累積和を計算しておき、遅延セグ木にかますときはそれを引いてから、1点取得するときにはそれを足してコンピュータ代を引いてから使う。このゴツい問題を通せたのは嬉しい。

ARC123

ABのスピードはこんなもん。Aはちゃんと考えて、Bは妥協して雑証明で済ます。CとDでDが難しそうな雰囲気なのでC寄りで交互に考える。C適当に書いて出すがダメ、Dはとりあえずダメそうなの提出して様子見てしばらく考えて時間切れ。

ABは普通だし、CDは悔しさすら感じないくらい何もわからなかった。もちろん嫌な気持ちにはなっている。なんでこんなにパフォ低いんだ?最近調子悪かったしなあ。頭の緩め方がわからない。

A - Arithmetic Sequence

正面からではきつそうなのでバラして操作の寄与を見るかと思っていると、問題文に等差数列の条件が書いてあってそれがそのまま使えた。この2数を等しくしたい。できる操作は3種類で、実質2種類。初期状態でどちらが大きいかで場合分けして、片方がやや面倒で差が5だったら3回2足して1引く感じに。それ以外に等しくする方法がないので解けている。

B - Increasing Triples

貪欲っぽい。全部ソートして小さいものから。一番小さいAから使うのはよさそう(どうせ小さいのは使う)。Cも小さいの使いたいよね(大きいの残したほうが得)。Bが怪しいが、大きいBを使ったほうがいいとして、それによるメリットは小さいBが残ることのみだからそれを誰かが使っているはずだが、それと入れ替えることができるので大きいBを使うメリットなさそう。これ以上は時間がかかりすぎるので、順位表見てから提出した。

ソートし忘れでサンプルが合わず少し時間食った。

C - 1, 2, 3 - Decomposition

かなり捉えどころがなく「ほんとに解けるのか?」という感じ。桁DPっぽく下からやりたいけど、めっちゃ上から繰り下げてくるの大変そうだし、上に行ったとき整合性が崩れて無理そう。DPしようにも、下の桁が何回でできるかは上の桁からの繰り下がりの量に依るので状態が爆発する。だいぶ経って、わからんけど上から考えてそれっぽいやつで実装した。必要条件として、上の桁ほど(1,2,3を引く)回数が少なくないといけない。(下限と上限を比較する感じで)それを満たさないとき繰り下げて、また最初からやり直す。最下位の桁を3で割って切り上げたものを出力する。WAだけど、わかんなかったので仕方ない。これを提出するくらいしかやることがないくらいわからなかった。答えが小さくなりそうな雰囲気はあったけど、それも証明できないので一応charからintにしてみたり。

(追記)42で一の位だけ見て1を出力するコードになってた。更新しながら使ってた下限の最大値を出力する。なんかAC。解説は頑張って読んだけど自分には難しかった。既視感は一応あるが、あれでO(log(N))になるの自分の感覚にない(上で爆発って書いたやつ(これも桁を分離して考えてて最近やった桁DPに引きずられてるんだよなあ))。

D - Inc, Dec - Decomposition

自分の実力では解けそうにない見た目をしている。とりあえず、BとCがどんどん離れていってしまいそうなのはわかる。差をできるだけ小さく保ちたいので、Aが大きくなったらBだけをいじり、Aが小さくなったらCだけをいじる。すると、このBCをいくつずらしたら最小になるかという問題になる。片方だけなら二分探索できるけど、BとCの和なのでたぶんダメ。前後50見たけど提出して半分くらいWA。正や非負の個数でどれだけ改善/悪化するか決まるんだけど、例えばB側でどこまで負にするか固定して考える?時間かかりそうで諦めたけど時間あったとしてもだめかあ。

(追記)非負整数tに対してBからt引いてCにt足すことを考える。グラフを描くとスコアは折れ線になっているのでその折れているところだけを探しても最小値は見つかる。全探索できそうだ。BCを(Cは符号反転してから)ソートする。累積和を計算しておく(ここがマジでオーバーフローギリギリ)。Bが { 1, 3, 5, 7 } のとき、t=5なら5以上の要素についてはフルに5ずつ絶対値を減らせる(5以上が何個目からかは二分探索(しゃくとりもできそうではあるけどさすがにね?))。5未満のとき、5以上の場合と比べて1減る毎に2だけ損する。全部tだったケースと比べてどれだけ減ってるかを累積和で計算する。アイデアはわりとすぐ思いついたけど、実装にかなり時間をかけた。解説は難しくてわからん。CDの解説異常に難しい(前提とする数学のレベルが高い気がする)。

ABC210

5完。結果は順当というところ。とにかく多くの問題をコンテスト中に考えたいので、30分残してF以外を通せたのは良い。

A - Cabbages

普通に場合分けしたけど1分45秒かかった。この実装はかなり重い。

B - Bouzu Mekuri

かなり怖い見た目をしているが、シミュレーションするだけで簡単。

C - Colorful Candies

難しい見た目。区間をずらしながらstd::mapで種類数の変化を監視。実装もけっこう大変。重いCだった。

D - National Railway

10^6個から2つ選ぶのは広い。1つめの駅の場所や2駅の距離を固定して考えてみるが間に合いそうにない。先にEをやった。

具体的な経路を考えていると、DPを思いついた。なんかデータ構造で答えが出る問題に見えちゃってたな。2駅が左上と右下の関係にあると仮定すれば、右か下にだけ動いて到達すればどんな経路でもいい。DPで、1つめの駅のコストとそこまでの移動コストの和の最小を計算していく(左上隅も含め全マスをINFで初期化し配るDP)(各マスでそれと2つめの駅のコストを足して最小値を更新していく)。左下と右上のケースもあるので、Aを上下反転してもう1回やる。わかってしまうと全然難しくはないんだよなあ。

E - Ring MST

少し考えると最小全域木だとわかる(気づかなかったけど問題名にMSTとあった)。つまりコストの低い辺から貪欲に使っていけばいい(コストが同じものが複数種類あった場合でもどんな順番でもいいのすごいよね)。まずコスト最小のA_iとNのgcdが1ならそこで終わり。そうでないとき、リングはA_i個で他の割り切れないやつ使えば全部つなげると思っちゃってWA。何も考えてない。ペナルティを避けるよりも、他の問題を考える時間を残したいと思っていた。リングはgcd(A_i, N)個だと気づいて提出してもWAで、まあ2WAになると"気づかされる"よね。今度は考慮に沈む。1回で全部つながるわけなくてgcd個まで減るだけだ。gcdが減ったぶんだけそこでコストを払う。ACして次へ。

F - Coprime Solitaire

どうやっていいか全然わからない。でもこれはABC。「典型キーワードは何だ?」と何度も問う。フローと2-SATを思いつく。最小カットだと「違うグループにしたらダメ」を表現できるけど、この問題はなんか違うっぽいのでしばらく考えて諦める。2-SATはACLにあるけど使ったことがない。頑張って考えるが、「複数あったらダメ」の扱いがわからなかった。悔しい。

夏アニメ第1話

今回は、好きな順ではなく、見た順。まあ、全部今日見たんだけど。どれがよかったかというと、1位はメイドラ確定で、他はそれぞれという感じかな。

ラブライブ!スーパースター!!

片方だけ全角なのキモすぎ。

日奈森あむっぽさを感じる。作画はいいが、キャラデザは好みでない(ラブライブプリキュアは似ているところがあると思っていて、自分に絶望的に合わない)。展開が読みやすいというか、予知できるわけではないけどわかりやすい(完全に子供向けアニメ)(難しい部分を全く受け取れていないのかもしれない)。つまらないということだけど、これは悪いこととは限らなくて、精神に負荷をかけずに流せるメリットがあるかも。ED、まあわかってはいたけどそれでもネタバレしすぎだろ。最後は意味不明だけど次回へのつなぎとして良い。ラブライブを初めて1話通して見た。

カノジョも彼女

ギャグの密度がすごい。作画はそんなによくないと感じたけど、押し切られてしまった。OP/EDの曲はわりとよさそう。

探偵はもう、死んでいる。

キャラは魅力的だがアニメがなんか気に入らない。(作画の)クオリティは高いのに(内容面で)ところどころあえて雑に流している。ED見てたら脚本赤尾でこで「え゛え゛っ」と声が出た。これは見る。そしてタイトルのことなんてすっかり忘れていた。

かげきしょうじょ!!

前半の男キャラで「ないな」と思った。途中から出てきたさらさだけ普通のアニメっぽい見た目と声。ED、ゆみり気づかなかった。双子の声が双子で感動した(どこに?)。九重クレアちゃんの人、特徴的な声出してるの初めて見た。ていうかさらさが一番上に表示されるんだ。

小林さんちのメイドラゴンS

面白すぎる。

トールってこんなキャラだっけ。小林さんはイメージ通り。まあだいぶ前だから忘れてるけど、「実はドラゴン」とか説明入ってるし、そもそも見始める前から「復習しなくても大丈夫」という信頼があった。OPで色々思い出す。5個の記号で区切るやつ1期でもあった気がする。サブタイトル表示が「ミニドラ」と同じ音楽でそこだけ見知った場所(そういえばミニドラ(少ししか見てないけど)でここに向けて俺の気分を盛り上げてくれてたんだな)。イルルの体型が少し違和感あるけど今回だけではなんとも。