Official

D - 蛍光ペンでマーキング / Highlighting with a Fluorescent Pen Editorial by kyopro_friends


この問題は、俗に imos 法と呼ばれる、累積和に関連するテクニックを用いて解くことができます。

問題文の指示通り、愚直に各マスがマーキングされた回数の配列を更新する場合、1回あたり \(\Omega(W)\) 時間かかるため、 \(K\) 回の操作全体では \(\Omega(KW)\) 時間かかり、実行時間制限に間に合わせることは困難です。

ここで、各マスがマーキングされた回数の配列そのものではなく、その階差数列を保持することを考えます。

階差数列:  0   1   0   0   0   0  -1   0   0       ↓累積和
元の数列:0   0   1   1   1   1   1   0   0   0              ↑階差

1 回のマーキングで階差数列が変化する箇所は高々 2 箇所であるため、 \(O(K)\) 時間で答えの階差数列を得ることができます。最後に \(O(N)\) 時間掛けて累積和を取ることで答えを得ることができます。

実装例 (C++)

#include<bits/stdc++.h>
using namespace std;

int main(){
  int n, w, k;
  cin >> n >> w >> k;
  vector<int>count(n+1);

  for(int i=0; i<k; i++){
    int l;
    cin >> l;
    l--;
    count[l]++;
    count[l+w]--;
  }

  for(int i=0; i<n; i++){
    count[i+1] += count[i];
  }
  for(int i=0; i<n; i++){
    cout << count[i] << ' ';
  }
}

実装例 (Python)

N, W, K = map(int, input().split())
count = [0] * (N+1)

for _ in range(K):
  l = int(input())
  l -= 1
  count[l] += 1
  count[l+W] -= 1

for i in range(N):
  count[i+1] += count[i]

print(*count[:-1])

posted:
last update: