公式

C - 連鎖停電 / Chain Blackout 解説 by physics0523


問題文が複雑ですが、慎重に読み指示に従うことでこの問題に正解できます。

実装では、変電所の耐久値からその変電所にかかる負荷を引いた値を管理した方が好都合です。今後、この値を変電所 \(i\) の HP と呼びます。

  • 最初、変電所 \(i\) の負荷は \(V_k=i\) なる \(k\) の個数なので、各送電線 \(i\) ごとに変電所 \(V_i\) の HP を \(1\) 減算します。
  • この段階で HP が負である場合、その変電所は停電します。停電する変電所は queue で管理することにします。
  • 以降、停電の連鎖を queue が空になるまで繰り返します。
    • 停電した変電所が伸びる先の変電所全てについて、それらの HP を \(1\) 減算します。
    • 減算の結果その変電所の HP が \(-1\) になった場合、その変電所が新たに停電します。よって、 queue に追加します。

以上の流れでシミュレートし、最後に HP が負である変電所を出力することで、この問題に正解できます。

実装例 (C++):

#include<bits/stdc++.h>

using namespace std;

int main(){
  int n,m;
  cin >> n >> m;
  vector<int> t(n+1);
  for(int i=1;i<=n;i++){ cin >> t[i]; }
  vector<vector<int>> g(n+1);
  for(int i=0;i<m;i++){
    int u,v;
    cin >> u >> v;
    g[u].push_back(v);
    t[v]--;
  }
  queue<int> q;
  for(int i=1;i<=n;i++){
    if(t[i]<0){
      q.push(i);
    }
  }
  while(!q.empty()){
    int od=q.front(); q.pop();
    for(auto &nx : g[od]){
      t[nx]--;
      if(t[nx]==-1){
        q.push(nx);
      }
    }
  }
  vector<int> res;
  for(int i=1;i<=n;i++){
    if(t[i]<0){res.push_back(i);}
  }
  if(res.empty()){cout << "-1\n";}
  else{
    for(int i=0;i<res.size();i++){
      if(i){cout << " ";}
      cout << res[i];
    }cout << "\n";
  }
  return 0;
}