Official

D - 安全な通学路 / Safe School Route Editorial by physics0523


まず、全ての頂点 \(v\) について \(d(v)\) を求めます。これは頂点 \(P\) を始点とした標準的な最短経路問題なので、ダイクストラ法を用いると求められます。

さて、これで \(d(v)\) を求めることができました。
パスの危険度 \(w\)\(d(v)\) の最小値で定義されています。なので、次の解法が成り立ちます。

  • 最初、全ての頂点について通行を禁止する。
  • \(d(v)\) が大きい頂点 \(v\) から順に、以下の操作を行う。
    • 頂点 \(v\) の通行を許可する。
      • 頂点 \(v\) に接続する辺のうち、行き先も通行が許可されているようなものの通行が許可されると捉えてもよい。
    • この時点で許可された頂点のみで \(S\) から \(T\) に到達できれば、危険度 \(d(v)\) のパスが存在すると判明し、終了する。

これは、 DSU(UnionFind) を用いることで実現可能です。

実装例 (C++):

#include<bits/stdc++.h>
#include<atcoder/dsu>

using namespace std;
using namespace atcoder;

using ll=long long;
using pl=pair<ll,ll>;
using Graph=vector<vector<pl>>;

const ll big=4e18;

int main(){
  ll N,M;
  cin >> N >> M;
  ll S,T,P;
  cin >> S >> T >> P;
  S--; T--; P--;
  Graph g(N);
  for(ll i=0;i<M;i++){
    ll U,V,W;
    cin >> U >> V >> W;
    U--; V--;
    g[U].push_back({V,W});
    g[V].push_back({U,W});
  }
  vector<ll> d(N,big);
  priority_queue<pl,vector<pl>,greater<pl>> pq;
  d[P]=0;
  pq.push({0,P});
  while(!pq.empty()){
    auto od=pq.top(); pq.pop();
    if(d[od.second]!=od.first){continue;}
    for(auto &nx : g[od.second]){
      if(d[nx.first] > d[od.second]+nx.second){
        d[nx.first]=d[od.second]+nx.second;
        pq.push({d[nx.first],nx.first});
      }
    }
  }

  vector<pl> vp;
  for(ll i=0;i<N;i++){
    vp.push_back({d[i],i});
  }
  sort(vp.rbegin(),vp.rend());
  dsu ds(N);
  vector<ll> exi(N,0);
  ll fl=0;
  for(auto &nx : vp){
    if(nx.second==S){fl|=1;}
    if(nx.second==T){fl|=2;}
    exi[nx.second]=1;
    for(auto &ny : g[nx.second]){
      if(exi[ny.first]){
        ds.merge(ny.first,nx.second);
      }
    }
    if(ds.same(S,T) && fl==3){
      if(nx.first>=big){cout << "INF\n";}
      else{cout << nx.first << "\n";}
      return 0;
    }
  }
  return 0;
}

posted:
last update: