ABC267 C - Index × A(Continuous ver.) Python解答例
AtCoder Beginner Contest 267 C - Index × A(Continuous ver.)をPythonで解きます。
問題
問題文をAtCoderのページより引用します。
問題文
長さ
の整数列 N が与えられます。 A=(A_1,A_2,\dots,A_N) 長さ
の M の連続部分列 A に対する、 B=(B_1,B_2,\dots,B_M) の最大値を求めてください。 \displaystyle \sum_{i=1}^{M} i \times B_i 制約
1 \le M \le N \le 2 \times 10^5 - 2 \times 10^5 \le A_i \le 2 \times 10^5 - 入力は全て整数。
解答例
数式変形と累積和
愚直に解くと
以下、プログラムを実装するときに混乱しないように、配列を0-indexedで表します。
すなわち、
とりあえず数式を変形することを考えます。整数列
これを用いると、求める解の式は以下のように変形することができます。
この式変形により、
累積和の配列
累積和を用いると、
となります。全ての
実装例
Pythonによる実装例を以下に示します。累積和はitertools.accumulate
を用いることで1行で記述できます。
from typing import *
import itertools
def main():
# 入力受け取り
N, M = map(int, input().split())
A: List[int] = list(map(int, input().split()))
# 累積和配列の作成
# 右半開区間[l, r)を扱うために先頭に0を付け加えている
C: List[int] = [0] + list(itertools.accumulate(A))
D: List[int] = [0] + list(
itertools.accumulate([(i + 1) * a for i, a in enumerate(A)])
)
# 整数kを全探索
# 今回の問題では負の数を扱うので、解の初期値には十分小さい負の値を入れておく
answer: int = -(1 << 60)
for k in range(N - M + 1):
X: int = D[k + M] - D[k]
Y: int = k * (C[k + M] - C[k])
answer = max(answer, X - Y)
# 答えの出力
print(answer)
if __name__ == "__main__":
main()
実際の提出はこちら。
-
長さ
の累積和配列N を作ったとき、整数C に対してl, r(0 \leq l < r < N) は左半開区間C_r - C_l の区間和となります。競プロの文脈だと左半開区間を扱うことはそんなに無く、むしろ右半開区間(l, r] に関心があることがほとんどです。右半開区間[l, r) の区間和を求めたいときは[l, r) とすればよいですが、添字ミスでバグを生みやすいので良い手法ではないです。C_{r - 1} - C_{l - 1}
そこで、先頭に0を付け加えた長さ の累積和配列N + 1 を用いると、上と同じ整数C' について右半開区間l, r の区間和を[l, r) で求めることができるようになります。こちらの方が簡潔に記述できるようになります。(どのみち添字の扱いには注意しなければなりませんが) ↩︎C'_{r} - C'_{l}
Discussion