Day 170-Q2 — 2026-10-05 | 3 乗コストの便分け(オンライン Monge DP。自分の dp 値を使う 1D/1D 漸化式を、決定単調性+二分探索の候補キューで O(N log N))

2026-10-05 赤色 Master (Phase 8+) ★★★★★★★★★ 高度 DP・DP 高速化(焦点帯 M1)。Day168-Q3 の分割統治最適化は「前の層の dp」を使う 2D/1D 型だった。今回は $dp[j]=\min_{i<j}dp[i]+w(i,j)$ と同じ配列を参照するので、行列の要素が計算の途中で決まる(オンライン)。分割統治をそのまま当てると使えない値を読む

問題

荷物が $N$ 個、到着順に並んでいて、$i$ 番目の重さは $a_i$。荷物を到着順のまま連続する区間に分け、各区間を 1 便で運ぶ。重さの合計が $W$ の便のコストは

$$C+W^3$$

($C$ は 1 便ごとの固定費、$W^3$ は積載量に対して急に増える費用)。全便のコストの合計の最小値を求めよ。

入力形式

N C
a_1 a_2 ... a_N

制約

#条件
1$1\le N\le2\times10^5$
2$0\le C\le10^{12}$
3$1\le a_i\le100$
4入力はすべて整数

入出力例

入力例1

5 10
1 2 3 2 1

出力例1

95

(1 個ずつ 5 便に分けて $5\cdot10+1+8+27+8+1=95$。$[1,2],[3],[2,1]$ の 3 便は $30+81=111$ で負ける。全 16 通りの区間分けの全探索で確認)

入力例2

3 37
1 1 2

出力例2

90

($[1,1],[2]$ で $37+8+37+8=90$。「最適な切れ目を前から順に進める 2 ポインタ」は 101 を出す最小の例)

入力例3

3 18
2 1 1

出力例3

52

($[2],[1,1]$ で $18+8+18+8=52$。「直前の最適な切れ目と 1 つ前だけ見る」近道は 64 を出す最小の例)

背景と出典

$w(i,j)=f(S_j-S_i)$($S$ は累積和、$f$ は凸)は Monge 性 $w(i,j)+w(i',j')\le w(i,j')+w(i',j)$($i\le i'\le j\le j'$)を満たし、$dp[j]$ の最適な $i$ は $j$ について単調になる(決定単調性)。$dp$ が同じ配列を参照する形は「1D/1D」「オンライン」の DP と呼ばれ、Galil–Park(1990 年ごろ)の整理や、候補をキューに持って境界を二分探索する方法が広く知られている、と私は理解している(原典は本セッションでは未確認)。$O(N)$ で解く LARSCH 法(Larmore–Schieber 1991)もあるが、本セッションでは実装・検証していない。

$f(W)=W^2$ なら Convex Hull Trick で済む($-2S_iS_j$ が $S_j$ の 1 次式になる)。3 乗では $S_j$ について 2 次の項が残るので直線の下側包絡にならず、CHT が使えない。だから「コストの形に依存しない」Monge の道具の出番になる。

概念図

左: 入力例 2(C=37, a=1 1 2)で j=3 の候補ごとの値 dp[i]+C+(S3−Si)^3 101i=0 102i=1 90i=2 2 ポインタ: i=1 で悪化したので i=0 に止まり 101 を出す 正解: i=2 の 90。i の関数は単峰ではない(最適の位置が単調なだけ) 右: 候補キュー。各候補が最良になる j の区間は左から右へ並ぶ j → i=0 i=3 i=5(末尾) 新しい候補 i=j を入れるとき: 1. 末尾の区間の先頭 b で j が勝つ → 末尾を丸ごと捨てる 2. 勝たない → 末尾の区間 [max(b, j+1), N] で j が勝ち始める位置を二分探索して末尾に追加 3. dp[j] は先頭の候補で計算(区間を過ぎたら先頭を進める) 根拠: Monge 性から「後の候補は一度勝てばずっと勝つ」 各候補 1 回入って 1 回出る → O(N log N)

ヒント(クリックで段階的に開示)

ヒント1(方向性)

$dp[j]=\min_{0\le i<j}\bigl(dp[i]+C+(S_j-S_i)^3\bigr)$。素直には $O(N^2)$。Day169-Q2 の SMAWK や Day168-Q3 の分割統治は「行列の全要素が最初から分かっている」ことを前提にしていた。ここでは $dp[i]$ が分かるのは $i$ を計算した後。

ヒント2(アプローチ)

候補 $i<i'$ について、「$i'$ が $i$ 以下になる $j$ の集合」は $j$ のある位置から右全部(Monge 性から)。だから各候補が最良になる $j$ の範囲は左から右へ並ぶ区間になる。候補を「(番号, 最良になる最初の $j$)」の列としてキューに持ち、新しい候補 $j$ を末尾に入れるとき、末尾の候補を区間の先頭で負かしていれば丸ごと捨て、そうでなければ区間の中で境界を二分探索する。

ヒント3(誘導)
for j in 1..N:
    先頭の候補の区間が j を過ぎていたら先頭を進める
    dp[j] = val(先頭の候補, j)
    while 末尾の候補 c の開始位置 b で val(j, b) <= val(c, b): 末尾を捨てる
    末尾の候補 last の区間 [max(b, j+1), N] で val(j, m) <= val(last, m) となる最小の m を二分探索
    m <= N なら (j, m) を末尾に追加

模範解答 (Python)

模範解答を表示する(まず自力で解いてから)
import sys


def main():
    data = sys.stdin.buffer.read().split()
    n, c = int(data[0]), int(data[1])
    S = [0] * (n + 1)
    for i in range(n):
        S[i + 1] = S[i] + int(data[2 + i])
    dp = [0] * (n + 1)

    def val(i, j):
        # 区間 (i, j] を 1 つの便にしたときの、j までの合計コスト
        w = S[j] - S[i]
        return dp[i] + c + w * w * w

    # cand[k] は候補 i、beg[k] はその候補が最良になる最初の j(beg は増加列)
    cand = [0]
    beg = [1]
    head = 0
    for j in range(1, n + 1):
        while head + 1 < len(cand) and beg[head + 1] <= j:
            head += 1
        dp[j] = val(cand[head], j)
        if j == n:
            break
        # 新しい候補 i=j を末尾に入れる。末尾の候補より beg で既に良いなら丸ごと消す
        while len(cand) > head + 1 and val(j, beg[-1]) <= val(cand[-1], beg[-1]):
            cand.pop(); beg.pop()
        last = cand[-1]
        lo = max(beg[-1], j + 1)
        hi = n + 1                      # [lo, hi) で「j が last 以下」になる最初の位置を探す
        while lo < hi:
            mid = (lo + hi) // 2
            if val(j, mid) <= val(last, mid):
                hi = mid
            else:
                lo = mid + 1
        if lo <= n:
            cand.append(j); beg.append(lo)
    print(dp[n])


main()

計算量: 各候補は 1 回入って 1 回出る。二分探索が 1 候補あたり $O(\log N)$ で全体 $O(N\log N)$。$N=2\times10^5$ で 1.49〜2.05 s(入力 3 種、Python 3.14.7、M1 Mac、実測)。答えは最大で約 $2\times10^5\cdot10^{12}+(2\times10^7)^3\approx8\times10^{21}$ になり得て 64 ビットを超える。Python の整数はそのまま扱えるが、C++ なら __int128 が要る。

Step-by-Step 解説

1なぜ Monge か

$f$ が凸なら、$x\le y$、$d\ge0$ で $f(x+d)-f(x)\le f(y+d)-f(y)$。$x=S_{i'}-S_i$… と置くと $w(i,j)+w(i',j')\le w(i,j')+w(i',j)$ が出る。$f(W)=W^3$ は $W\ge0$ で凸。

2「後から来た候補は、一度勝てばずっと勝つ」

Monge 性を $i<i'$、$j<j'$ に使うと、$dp[i']+w(i',j)\le dp[i]+w(i,j)$ なら $j'$ でも $i'$ が勝つ。だから候補の勝つ範囲は区間で、後の候補ほど右にある。キューの末尾を捨てる条件と二分探索の単調性は、この 1 行から出る。

3分割統治最適化がそのまま使えない理由

Day168-Q3 の分割統治は $j$ の中央から計算するが、中央の $dp[mid]$ を計算するには $dp[i]$($i<mid$)がすべて確定している必要がある。オンラインでは成り立たない。直すには、$[l,m)$ を確定させてから $[l,m)\to[m,r)$ の寄与を分割統治で配る CDQ 分割統治にする($O(N\log^2N)$、他案の表)。

42 ポインタが誤る理由

最適な $i$ は単調だが、$i$ についての関数 $dp[i]+w(i,j)$ は単峰とは限らない。「次の候補が悪くならない間だけ進める」2 ポインタは谷の手前で止まる。ランダム 500 件で 60 件誤答(実測)、最小の例が入力例 2。

迷った他案と採らなかった理由

案計算量採否理由
全 $i$ を見る素直な DP$O(N^2)$検算用のみランダム 500 件の正解に使用
CHT(直線の下側包絡)$O(N)$不採用コストが $S_j$ の 1 次式にならない(3 乗)
2 ポインタ(単峰を仮定)$O(N)$不採用60/500 誤答
直前の最適と $j-1$ だけ見る$O(N)$不採用151/500 誤答
CDQ 分割統治+monotone minima$O(N\log^2N)$次点500 件一致、$N=2\times10^5$ で 3.32 s(本解の約 2 倍)
候補キュー+二分探索(本解)$O(N\log N)$採用500 件一致、1.49〜2.05 s、実装が短い
LARSCH$O(N)$不採用実装が長く、本セッションで検証していない。$N=10^6$ 以上で定数倍が効くなら検討

前提が変わったら:

  • $f(W)=W^2$ なら CHT で $O(N)$
  • $f$ が凹(大口割引)なら Monge の不等式が逆向きになり、本解の「後の候補が勝ち続ける」が成り立たない。候補キューの向きを反転させた別の手法(スタック+二分探索)が要る
  • 便の数がちょうど $K$ なら、$K$ 層の 2D/1D になり分割統治最適化(Day168-Q3)か Alien DP

よくあるミス

ミス原因正しい書き方
分割統治最適化をそのまま使うまだ計算していない $dp[i]$ を読むCDQ にするか、候補キュー
2 ポインタで最適な $i$ を進める決定単調性と単峰性を混同二分探索で境界を求める(60/500 誤答)
二分探索の左端を beg[-1] のままにする$j$ 以下の位置で $val(j,\cdot)$ を評価してしまうmax(beg[-1], j+1)
CHT を当てる$W^2$ の問題と同じだと思う3 乗は 1 次式に分解できない
C++ で long long$(2\times10^7)^3=8\times10^{21}$__int128

次のステップ

  • 発展1: 同じ問題を LARSCH で $O(N)$ にし、$N=10^6$ で本解と速さを比べる
  • 発展2: コストが $C+\sqrt W$(凹)のときの 1D/1D を、スタック版で解く
  • 発展3: 便の数をちょうど $K$ にした版を Alien DP(Lagrange 緩和)で解き、$C$ を二分探索する

検証ログ(本セッションで実行した数値)

  • 素直な DP との照合(実測): $N\le40$、$a_i\le3/10/100$、$C$ を 3 段階に振ったランダム 500 件で本解・CDQ とも不一致 0
  • 誤った方法(実測): 同じ 500 件で 2 ポインタ 60 件、直前の最適と $j-1$ だけ見る方法 151 件誤答。最小の反例(20 万件のランダム探索)が入力例 2・3
  • 大入力の一致(実測): $N=2\times10^5$ の 3 種と $N=10^5$ の 1 種で本解と CDQ の答えが一致(例: 1908029859298045、3779848720、51122579703、955582065011705)
  • 実行時間(実測): 本解は $N=2\times10^5$ で 1.66 s($C=10^{12}$)、1.49 s($a_i=1$)、2.05 s($C=0$)、$N=10^5$ で 0.74 s。CDQ は $N=2\times10^5$ で 3.32 s、$N=10^5$ で 1.68 s
  • 入力例(実測): 例 1 95、例 2 90、例 3 52
  • 扱っていない範囲: LARSCH の実装、凹コスト版、Galil–Park・Larmore–Schieber の原典

自己評価

自分の回答

気づき・メモ