読者です 読者をやめる 読者になる 読者になる

AGC008B Contiguous Repainting

競技プログラミング AtCoder
  • 概要

N個の整数列が与えられる。連続するK(<=N)個を選び、全てを白で塗るか全てを黒で塗る(この操作は何回でもおこなうことができる)。色は上書きされる。 黒く塗られた整数の総和の最大値をもとめよ

  • 方針

操作順を逆に考える。

そのまま)何回かK個を塗る操作をおこなったあと、最後にK個を白or黒で塗る操作を行う。
逆)最後の操作で塗ったK個の色が確定させ、次の最後から二番目の操作で塗った色を確定させる。(色は上書きされない)  

K区間以外は自由な色に確定させることが可能。のでKの区間を探索し、区間の和が>0なら黒、そうでなければ白とする。それ以外の区間はai > 0なら黒、そうでなければ白とする。

単純にやると計算量がO(N2)でアウト。そこで、単純な累積和(=ai)と、0より大きい要素のみの累積和(=bi)を予め求めておく。すると、Kの区間が決まると、K区間の和はaiを使ってO(1)、それ以外の区間の和もbiを使ってO(1)で求まる。よって計算量O(N)でいける。

#include <iostream>
#include <vector>
using namespace std;
 
typedef long long ll;
int main() {
    int n, k;
    cin >> n >> k;
    vector<ll> v(n+1), a(n+1, 0), b(n+1, 0);
    for(int i=1;i<n+1;i++) cin >> v[i];
 
    for(int i=1;i<n+1;i++) {
        a[i] += v[i] + a[i-1];
    }
    for(int i=1;i<n+1;i++) {
        if(v[i] > 0)
            b[i] += v[i] + b[i-1];
        else
            b[i] = b[i-1];
    }
 
    ll ans = -1e15;
    for(int i=1;i <= n - k + 1;i++) {
        ll ans_i = 0;
        ll k_range_sum = a[i+k-1] - a[i-1];
 
        ans_i += max<ll>(k_range_sum, 0);
        ans_i += b[i-1];
        ans_i += (b[n] - b[i+k-1]);
        ans = max<ll>(ans, ans_i);
    }
 
    cout << ans << endl;
}