Official

C - 花壇の水やり / Watering the Flower Bed Editorial by kyopro_friends


この問題は階差に注目することで解くことができます。

区画 \(i\) の水分量の不足を \(C_i=B_i-A_i\) と定めます。これにより問題は次のように読み替えられます。

整数列 \(C=(C_1,\ldots,C_N)\) が与えられる。「\(i\) を選び、 \(C_i,C_{i+1},\ldots,C_{i+K-1}\) それぞれから \(1\) を引く」という操作を行って、全て \(0\) にするための最小回数は?

\(C\) が負の要素を含む場合不可能です。以下、\(C\) の全ての要素は \(0\) 以上とします。

\(C_0=C_{N+1}=0\) と定め、 \(C\) の階差数列を \(D_i=C_{i+1}-C_{i}\) \((0 \leq i \leq N)\)と定めます。 \(C\) の全ての要素が \(0\) であることは、 \(D\) の全ての要素が \(0\) であることと同値です。また、「\(i\) を選び、\(C\) の連続する \(K\) 個の要素 \(C_i,C_{i+1},\ldots,C_{i+K-1}\) それぞれから \(1\) を引く」という操作は「\(i\) を選び、\(D_i\) から \(1\) を引き、\(D_{i+K}\)\(1\) を加える」と同値です。

例:
図

\(D_0\) を変更する操作は \(i=0\) しか存在しないため、 \(i=0\) で操作を行う回数は \(D_0\) から一意に定まります。その後、 \(D_1\) について考えることで、同様にして左から貪欲に操作回数が一意に決まります。各 \(i\) に対する操作は \(O(1)\) で行えるため、全体で \(O(N)\) でこの問題を解くことができます。

実装例 (C++)

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

int main(){
  int n, k;
  cin >> n >> k;
  vector<int> a(n), b(n);
  for(int i=0; i<n; i++) cin >> a[i];
  for(int i=0; i<n; i++) cin >> b[i];

  vector<int> c(n+2);
  for(int i=0; i<n; i++){
   c[i+1] = b[i] - a[i];
  }
  vector<long long> d(2*n);
  for(int i=0; i<=n; i++){
    d[i] = c[i+1] - c[i];
  }

  long long ans = 0;
  for(int i=0; i<n; i++){
    if(d[i] < 0){
      cout << -1 << endl;
      return 0;
    }
    ans += d[i];
    d[i+k] += d[i];
    d[i] = 0;
  }

  for(int i=0; i<2*n; i++){
    if(d[i] != 0){
      cout << -1 << endl;
      return 0;
    }
  }
  cout << ans << endl;
}

実装例 (Python)

N, K = map(int, input().split())
A = list(map(int, input().split()))
B = list(map(int, input().split()))

C = [0] + [b - a for a, b in zip(A, B)] + [0]
D = [C[i+1] - C[i] for i in range(N+1)] + ([0] * N)

ans = 0
for i in range(N):
  if D[i] < 0:
    print(-1)
    exit()
  ans += D[i]
  D[i+K] += D[i]
  D[i] = 0

if all(d==0 for d in D):
  print(ans)
else:
  print(-1)

posted:
last update: