考え方
分割の過程を二分木だと思って逆順に見ると,各葉に起因するコストは(葉の長さ)×(葉の深さ)となる.よって,もし二分木の形が決まっているのなら,長さが小さいものが最も深くなるように配置すれば良い.
以上をもとに,テキトーにつくった(分割過程を表す)二分木のコストを作り変え,最小化する手順を考える.これは,
- コストが最小の2つが二分木の最深部に位置するように並び替える
- ↑の2つの葉の親を新たな葉と考え直す
- 以下同じ
よって,バラバラの状態から考えて,一番小さい2つを順にくっつけていけばよい.
(ちゃんと示すなら帰納法を使うが,実際の発見手順はこんな感じ?)
回答例
from heapq import heappop, heappush, heapify N, L = map(int, input().split()) A = list(map(int, input().split())) SUM = sum(A) if SUM < L: A = A + [L - SUM] heapify(A) ans = 0 while len(A) > 1: x = heappop(A) y = heappop(A) ans += x + y heappush(A, x + y) print(ans)
参考
アライグマ「F問題はマージテクなのだ! バラバラのパンを1つにまとめる問題だと思って、小さいパン2つをくっつけていけばいいのだ!」
— 競技プログラミングをするフレンズ (@kyopro_friends) May 21, 2022
フェネック「ABC247Exの想定解法やABC215GのO(N(logN)^2)解法みたいに、次数の合計がNである多項式の積を求めるときに使う考え方だよ。多項式のマージテクはもっと手抜きをしても同じ計算量にできるけどねー」
— 競技プログラミングをするフレンズ (@kyopro_friends) May 21, 2022
フェネック「最小2つを貪欲にマージしていいことの証明は、「マージの過程を二分木だと思ったとき、コストは葉の重み×深さの和になるから、二分木を決めたとき、一番深いところは最小の要素と2番目に小さい要素をマージしているとしてよく、要素数が1減ったから帰納法」って感じでいいはずだよー」
— 競技プログラミングをするフレンズ (@kyopro_friends) May 21, 2022