競技プログラミング日記

主に AtCoder の記事です

AtCoder Beginner Contest 204E

ABC204E

コストが変則な Dijikstra. 時刻 \(t\) に 頂点 \(v\) を出発すると,次の頂点に到達する時刻が \(f(t) := c + \lfloor d/(t+1) \rfloor\) となる. 出発するときに待てば良いので,それ以外のタイミングで待つ必要はない.
簡単のため floor を無視して考えると, およそ \(sqrt(d)\) の前後で \(f(t)\) は最小となる. \(v\) に到達した最小の時刻を \(t_{0}\) とすると, \(f(t)\) が最小となる \(t\) の候補は,十分大きな定数 \(Y\) に対して \(t = t_{0}\) または \(max(sqrt(d) - Y, t_{0}) \leq t \leq sqrt(d) + Y\) のどれか. これを全探索すれば良い.

実装
変数に似た名前を使うときは注意.

使っている記号,マクロ等 "https://ecsmtlir.hatenablog.com/entry/2022/12/23/131925"

int main() {
  ll n, m ;
  cin >> n >> m;
  vll a(m), b(m) , c(m), d(m);
  vector<vector<tuple<ll,ll,ll>>> to(n);
  rep(i,m){
    cin >> a[i] >> b[i] >> c[i] >> d[i];
    a[i]--, b[i]--;

    to[a[i]].emplace_back(b[i], c[i], d[i]);
    to[b[i]].emplace_back(a[i], c[i], d[i]);
  }

  min_priority_queue<pll> q;
  vll dis(n, INFL);

  q.push({0,0});
  chmin(dis[0], 0LL);
  while(q.size()){
    auto [cdis, cv]  = q.top(); q.pop();
    if(dis[cv] != cdis) continue;

    for(auto [nv, nc, nd] : to[cv]){
      ll ndis = cdis + nc + nd/(cdis+1);
      ll sq = sqrt(nd);
      ll C = 100;

      srep(i, sq-C, sq+C+1) if(i >= cdis){
        chmin(ndis, i + nc + nd/(i+1));
      }

      if(chmin(dis[nv], ndis)){
        q.push({ndis, nv});
      }
    }

  }

  ll ans = dis[n-1];
  if(ans >= INFL) ans = -1;
  cout << ans << endl;  


  return 0;
}