15 分くらい間に合わなくて悔しい。
解法
以下 0-indexed であるとする。
とする。
まず数列 はソートしても良い。 また、相対的な差にしか興味がないため、 であるとして良い。
数列の差分を図示して、各差分の寄与を から逆順に考えると以下のようになる。(以下は の場合である。)
数列のある隣接要素の差分を 増やすと、その差分の寄与だけ が増加する。(例えば なら寄与は または のいずれかとなる。)
この寄与は上記図の計算方法からわかるように、差分 は が 回だけ足されているため と表現することができる。
はソートされているため、 であり、 は差分の総和である。
最終的には を最小化するため、 となるように隣接要素の差分を 増やす操作の回数を最小化すると言い換えることができ、これはナップザック問題を解けば良い。
とすると、試すべき は 個あるため 全体の計算量は一見 となりそうだが、実は試すべき は 個で抑えられるため である。
理由
となる についてのみ考えれば良い。
のとき、 より は 個。
のとき、 を解くと
で、対称性より となる だけ見れば良い。
ここで
なので は 個。
#include "my_template.hpp" using namespace std; void solve() { INT(N, L); VEC(int, A, N); constexpr int M = 200000; vector<int> dp(M + 1, INF<int>); dp[0] = 0; REP(k, L - 1) { i64 w = (k + 1) * (L - 1 - k); REP(j, M + 1 - w) chmin(dp[j + w], dp[j] + 1); } REP(i, N) { int ans = dp[A[i]]; if (ans == INF<int>) ans = -1; print(ans); } return; } int main() { solve(); return 0; }
感想
がソート済みのとき、 というのがかなり本質な気がする。
ChatGPT でこの式変形を導出できた人のツイートが TL に流れてきてびっくりした。(自分のChatGPTはまるで役に立たなかった。)
C 、ChatGPT に投げると本質が返ってきた pic.twitter.com/ZLdTUyXqJN
— みなと (@minato2376) 2024年5月19日