writer: くる / ningenMe
解説です。
- A - Omiyage
- B - Plus Or Multiple Operation
- C - Encounter On A Tree
- D - Make One With GCD
- E - LISGRID
- F - You Are A Project Manager
A - Omiyage
https://yukicoder.me/problems/2842
部分和を行い、使えるお金のうち以下の最大値を求めます。
番目の国まで訪れてつずつおみやげを買ったときに円使えるかどうか。
これは計算量 で解くことができます。
writer解はこちら。
https://yukicoder.me/submissions/391792
B - Plus Or Multiple Operation
https://yukicoder.me/problems/3389
まず のときはを作ることができません。その他の場合では必ずを作ることができます。
進展開を考えます。進数で桁(0-indexed)であるとき と書けます。
ここで倍する操作は進数において桁シフトする操作、を足す操作は桁目にを足す操作に言い換えることができます。
よって桁の大きい方から「和の操作」によって数字を揃え、「積の操作」によって桁を上げることを貪欲に繰り返すことで、最適なコストで数を作ることができます。
ただし最初の回の操作に関しては、和の操作で起きる繰上りにより桁シフトを行うことができ、この場合の方がコストが少ないときがあります。
具体的にはとの大小関係によってこのコストが変化します。
1クエリあたりで計算することができるため、全体として計算量で解くことができます。
【補足】
上記の操作が最適なことに関しての補足を以下に記します。(主にテスターのtpyneriverさんが証明してくれました、ありがとうございます)
数xにする最小手順をとすると以下が成り立ちます。
(1)
(2)
(3)
のときが成り立つと仮定する
のときとすると(2)よりなるが存在する。
またなるが存在する。
これらよりなるが存在する。
ここでであることと(2)より
が成立するのでが成立。
しかし(2)よりなので矛盾する。
よってでが成り立つときでが成立。
また(2)より常にであることから となりが成立。
以上よりが成り立つときが成り立ち、よりのとき常にが成り立つ。
(4)
でが成立すると仮定する。
まずについて、がの倍数でないので(2)より
同様にでが成立。
よってでが成立するときでが成立する。
が成立するので、で(4)は成立する。
以上より最適手順として
と遷移がわかる。
writer解はこちら。
https://yukicoder.me/submissions/392103
C - Encounter On A Tree
https://yukicoder.me/problems/2827
「すべてのについて、頂点の深さが頂点の深さよりも大きいならば、頂点に書き込まれる番号は頂点に書き込まれる番号より大きい」という条件から深さの頂点に書き込まれるような数字の候補の集合をとすると、となります。
よってある数字が書きこまれるとき、その頂点の深さは一意に決まることが分かります。
が書き込まれた頂点の深さをそれぞれとし、またが書き込まれた頂点の(最小共通祖先)の頂点の深さをとします。
長さのパスが存在する場合かつが成り立ちます。
このようなである頂点の候補は通りあり、その1つのに対してが書き込まれるような数字の候補は「の子を根とする部分木」を考えると通りあります。
同様にが書き込まれるような数字の候補は通りあります。
またの子は左右の2通りがあるため、パスの長さがとなるようなの書き込み方は通りとなります。
残りの以外の書き込み方に関しては、各深さごとに独立に候補の数字の順列を考えればよく、これは簡単に計算できます。
実装の際はとなる場合のコーナーケースに注意してください。
全体として計算量で解くことができます。
writer解はこちら。
https://yukicoder.me/submissions/392120
D - Make One With GCD
https://yukicoder.me/problems/3305
愚直に全探索をすると通りの場合があり、とても間に合いません。そこで を行います。
から見ていって最後にを使って最大公約数がになるような場合の数。
ここでに関してですが、なので愚直に状態を持つと空間計算量、時間計算量ともに制約をオーバーしてしまいます。
しかしを用いた最大公約数は必ずの約数となり、以下の数字の約数の個数は高々個であることから無駄な状態を減らすことで十分高速に計算できます。
つの状態に対して遷移が個あるので、以下の数字の約数の個数の最大値をとすると、計算量 で解くことができます。
writer解はこちら。
https://yukicoder.me/submissions/392116
E - LISGRID
https://yukicoder.me/problems/3092
最初に数列とを昇順にソートします。
そして各行、各列において「前半は単調減少な数列、後半は単調増加な数列」になるように数列を構築することを考えます。ここで単調増加な部分に関しては題意の条件を満たすようにします。
するとこのとき、各マスにおける隣接マスとの大小関係が決まります。
この大小関係を有向辺とみなすことでトポロジカル順序が決まるため数字を矛盾なく書き込むことができます。トポロジカルソートしたときのスタートの候補となる頂点は最大でもの4つが存在しますが、を最初にスタートとしてから順に書き込み、最後にをスタートとして用いることで問題の条件に合うグリッドを構築することができます。
ソートと書き込み時の探索を考慮すると、計算量 で解くことができます。
writer解はこちら。
https://yukicoder.me/submissions/392115
F - You Are A Project Manager
https://yukicoder.me/problems/3227
を決め打ちすることを考えます。
すると左右からぞれぞれの倍数だけプログラマを選ぶ操作としてみなすことができるので、そのときの区間の中央値の塁積和を前計算しておくことで左からチーム、右からチーム作ったときの行動力の最大値を求める問題になります。
またこの値は塁積和をとった値に対してさらに累積maxをとることで、の分割を決める位置を探索することに言い換えれます。
と動かしたときに、中央値を求める区間の数に関しては調和級数的に状態が小さくなるため、全体で個になります。
よってこの個の区間の中央値が分かると、について全探索することでこの問題が解けます。
すなわち区間の大きさ、クエリ数で区間の中央値をオフラインで答える問題に言い換えることができました。
この中央値を愚直に求めると計算量がとなりとても間に合いません。
しかし区間を平方分割し、Mo's Algorithmを利用することで区間を伸縮させながら高速に中央値を求めることができます。
区間の伸縮の際は(c++における)multisetを2本持つことで、で中央値を管理することができます。
よって必要な区間の中央値をで求めることができます。
また全体としても計算量で解くことができます。
writer解はこちら。
https://yukicoder.me/submissions/392113