Official

C - 観覧車の眺め / View from the Ferris Wheel Editorial by MMNMM Editorial for printing


この問題は、見えるゴンドラを何番目からにするかを全探索することで解くことができます。

それぞれの場合に見える \(M\) 個のゴンドラの美しさを毎回足していては時間計算量が \(\Theta(NM)\) などになってしまい、実行時間制限に間に合わせるのは難しいです。

ここで、次のような工夫をすることで実行時間を全体で \(O(N)\) 時間などにすることができます。

  • \(i\) 番目から連続する \(M\) 個を見るときと、\(i+1\) 番目から連続する \(M\) 個を見るときでは、\(i\) 番目のゴンドラと \(i+M\) 番目(ここで、\(i+M\gt N\) のとき \(i+M-N\) 番目のゴンドラを指すこととします)のゴンドラだけ見えるか見えないかが変わります。よって、\(i\) 番目から連続する \(M\) 個のゴンドラを見たときの合計の美しさから \(A _ i\) を引いて \(A _ {i+M}\) を足すことで、\(i+1\) 番目から連続する \(M\) 個のゴンドラを見たときの合計の美しさを求めることができます。
  • 連続する \(M\) 個のゴンドラは、\(A\) においてたかだか \(2\) つの区間になります。よって、\(A\) の累積和を求めておくことで \(O(1)\) 時間で合計の美しさを求めることができます。
    • \(A\) の末尾に \(A _ {N+1}=A _ 1,A _ {N+2}=A _ 2,\ldots,A _ {N+M-1}=A _ {M-1}\) を追加しておくことで、\(1\) つの区間にすることもできます。

いずれの方法も十分高速です。

実装例は以下のようになります。

#include <iostream>
#include <vector>
using namespace std;

int main() {
    int N, M;
    cin >> N >> M;
    vector<long> A(N);
    for (long& a : A) {
        cin >> a;
    }

    // 先頭 M - 1 個をコピーして
    for (int i = 0; i + 1 < M; ++i) {
        A.emplace_back(A[i]);
    }
    // 累積和を計算
    A.emplace(A.begin());
    for (int i = 1; i < N + M; ++i) {
        A[i] += A[i - 1];
    }

    long ans = -500000000000000;
    for (int i = 0; i < N; ++i) { // どこから見始めるかを全探索し
        ans = max(ans, A[i + M] - A[i]); // 最大値が答え
    }
    cout << ans << endl;
    return 0;
}
N, M = map(int, input().split())
A = list(map(int, input().split()))

# 先頭 M - 1 個をコピーして
A = [0] + A + A[:M-1]
# 累積和を計算
for i in range(1, N + M):
    A[i] += A[i - 1]

ans = -500000000000000
for i in range(N): # どこから見始めるかを全探索し
    ans = max(ans, A[i + M] - A[i]) # 最大値が答え

print(ans)

posted:
last update: