直接総和計算 による O(m) 実装 — 差分配列不要の最適解
n 個のキャンディ瓶(初期値
0)に対して
m 回の操作を行う。
各操作
[a, b, v]
は 「インデックス a 以上 b 以下の全瓶に v 個追加」を意味する。
全操作後の
平均キャンディ数の床関数
を返せ。
| 操作 | 瓶1 | 瓶2 | 瓶3 | 瓶4 | 瓶5 |
|---|---|---|---|---|---|
| 初期 | 0 | 0 | 0 | 0 | 0 |
| [1,2,100] | 100 | 100 | 0 | 0 | 0 |
| [2,5,100] | 100 | 200 | 100 | 100 | 100 |
| [3,4,100] | 100 | 200 | 200 | 200 | 100 |
| 合計→平均 | 800 ÷ 5 = 160 ✅ | ||||
from __future__ import annotations
import os
from typing import List
# ─────────────────────────────────────────────────────────
# 競技プログラミング向け実装
# Time: O(m) ← 操作数のみ(n に非依存)
# Space: O(1) ← 追加配列なし
#
# 核心式:
# S = Σ v_i × (b_i - a_i + 1)
# answer = floor(S / n) = S // n
# ─────────────────────────────────────────────────────────
def solve(n: int, operations: List[List[int]]) -> int:
# Δ S = v × (b - a + 1) を全操作分加算
total: int = sum(v * (b - a + 1) for a, b, v in operations)
# S // n は floor(S/n) と等価(S≥0, n>0 保証)
return total // n
# ─────────────────────────────────────────────────────────
# 業務開発向け実装(型安全・バリデーション付き)
# ─────────────────────────────────────────────────────────
def solve_production(n: int, operations: List[List[int]]) -> int:
"""
全操作後の平均キャンディ数の床関数を返す。
Args:
n: 瓶の数 (1 ≤ n ≤ 10^7)
operations: [[a, b, v], ...] 形式の操作リスト
Returns:
floor(全瓶の平均キャンディ数) の整数値
Raises:
ValueError: n が正でない、または操作が制約違反の場合
"""
if not isinstance(n, int) or n <= 0:
raise ValueError(f"n は正の整数である必要があります: {n}")
if not operations:
return 0
total: int = 0
for op in operations:
if len(op) != 3:
raise ValueError(f"操作は 3 要素のリストである必要があります: {op}")
a, b, v = op
if not (1 <= a <= b <= n):
raise ValueError(f"無効なインデックス範囲 [{a}, {b}](n={n})")
if v < 0:
raise ValueError(f"v は非負である必要があります: {v}")
total += v * (b - a + 1) # Δ S = v × (b - a + 1)
return total // n # floor(S / n)
# ─────────────────────────────────────────────────────────
# HackerRank エントリポイント
# ─────────────────────────────────────────────────────────
if __name__ == '__main__':
fptr = open(os.environ['OUTPUT_PATH'], 'w')
first_multiple_input = input().rstrip().split()
n = int(first_multiple_input[0])
m = int(first_multiple_input[1])
operations: List[List[int]] = []
for _ in range(m):
operations.append(list(map(int, input().rstrip().split())))
result = solve(n, operations)
fptr.write(str(result) + '\n')
fptr.close()
フローの解説:
1. 入力受付:
n(瓶の数)、m(操作数)、全操作リストを受け取る
2. 初期化: 総和 S を 0 で初期化(追加配列は不要)
3. 操作ループ: 各操作の寄与 Δ S = v × (b - a + 1) を S
に加算
4. 核心式: 差分配列展開をスキップ、O(m)
で完結する直接総和計算
5. 床関数: S // n で平均の floor を計算
6. 出力: 結果を返す
| アプローチ | 時間計算量 | 空間計算量 | 備考 |
|---|---|---|---|
| ✅ 直接総和(本解) | O(m) | O(1) | n に非依存。最適解。 |
| 差分配列 | O(n + m) | O(n) | n=10⁷ で無駄に遅い |
| 愚直シミュレーション | O(n × m) | O(n) | 最大 10¹² 操作 → TLE |
差分配列は各瓶の個別の最終値を求めるときに必要。
今回必要なのは総和だけ。
総和 = Σ(各瓶の値)= Σ 操作の寄与
= Σ v × (b - a + 1)
これは O(m) で直接計算できる。
最大総和 = 10⁹ × 10⁷ × 10⁵ = 10²¹
CPython の int は任意精度
bignum なので
オーバーフローの心配は一切不要。
// 演算子は bignum
にも正確に動作する。