Official

D - 電球パネルの一斉制御 / Simultaneous Control of Light Bulb Panels Editorial by kyopro_friends


整数 \(i\) を選んで行う操作を操作 \(i\) と呼ぶことにします。

操作の順序を入れ替えても結果に影響しません。また、どの操作も同じ操作を \(2\) 回行うと、操作を行わなかった場合と同じ結果になります。よって、どの \(i\) についても操作 \(i\) を行う回数は \(0\)\(1\) です。

電球 \(1\) の状態を変化させることができるのは、 操作 \(1\) を行ったときのみです。よって初期状態で電球 \(1\) が点灯しているときは操作 \(1\) を行い、消灯しているときは操作 \(1\) は行いません。
電球 \(1\) を消灯させたあと、他の操作は電球 \(1\) に影響を及ぼさないため、電球 \(1\) のことは忘れて残った \(N-1\) 個の電球に対して再び同じ問題を考えればよいです。

よって電球 \(1,2,3,\dots\) の順に電球の状態を見ながら操作 \(1,2,3,\dots\) を行うかどうかを決めるのが最適です。しかし実際に電球の状態を変化させる操作を行うと、操作 \(1\) 回あたり \(\Omega(K)\) の時間がかかるため、最悪ケースでは全体で \(\Omega(NK)\) となり、実行時間制限に間に合わせることは困難です。

そこで、各電球の隣との差に注目します。消灯している電球 \(0,N+1\) を追加し、「電球 \(i-1\) と電球 \(i\) の点灯/消灯の状態が異なるような \(i\) 」全体からなる集合を用意します。
操作 \(i\) を行うと、「電球 \(i-1\) と電球 \(i\)」「電球 \(i+K-1\) と電球 \(i-K\)」の差だけが変化します。よって、高々 2 個の要素の出し入れにより操作をシミュレートすることができます。全ての電球が消灯していることは、この集合が空であることと同値になります。

実装例 (C++)

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

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

  set<int>s;
  for(int i=1;i<=n+1; i++){
    if(a[i-1] != a[i]){
      s.insert(i);
    }
  }

  int count = 0;
  for(int i=1; i<=n-k+1; i++){
    if(s.contains(i)){
      count++;
      s.erase(i);
      if(s.contains(i+k)){
        s.erase(i+k);
      }else{
        s.insert(i+k);
      }
    }
  }

  if(s.size() == 0){
    cout << count << endl;
  }else{
    cout << -1 << endl;
  }
}

実装例 (Python)

N, K = map(int, input().split())
A = [0] + list(map(int, input().split())) + [0]
S = set()
for i in range(1, N+2):
  if A[i-1] != A[i]:
    S.add(i)

count = 0
for i in range(1, N-K+1+1):
  if i in S:
    count += 1
    S.remove(i)
    if i+K in S:
      S.remove(i+K)
    else:
      S.add(i+K)

if len(S) == 0:
  print(count)
else:
  print(-1)

posted:
last update: