解法
現在時刻を とすると、ダイクストラ法で各遷移において となる最小の を求めることができれば良いので線形合同式が解ければ良い。
しかしこれを解くのは意外と難しく、法が素数ではないときに逆元がないが が解を持つという場合がある。(例: に対し )
なので tourist 氏や maspy 氏のコードを参考に ライブラリ を作成した。
結局のところ が解ければ良いのだが、解 が求められたときに の周期が ではなく であるという点に注意する。
#include "my_template.hpp" #include "graph/read_graph.hpp" #include "math/linear_diophantine.hpp" using namespace std; void solve() { I64(N, M, H); VEC(i64, L, N); VEC(i64, S, N); auto g = read_graph<int>(N, M); vector<i64> dp(N, INF<i64>); pqueg<pair<i64, int>> que; dp[0] = -1; que.emplace(-1, 0); while (!que.empty()) { auto [t, i] = que.top(); que.pop(); if (t != dp[i]) continue; FORE(e, g[i]) { int j = e.to; // L[i] + S[i] * x ≡ L[j] + S[j] * x (mod H) となる x を求める // (S[i] - S[j]) * x ≡ L[j] - L[i] (mod H) auto [x, mod] = linear_congruence(S[i] - S[j], L[j] - L[i], H); if (mod == -1) continue; i64 nt = (t + 1); if (nt % mod <= x) { nt = nt / mod * mod + x; } else { nt = ceil(nt, mod) * mod + x; } if (chmin(dp[e.to], nt)) { que.emplace(dp[e.to], e.to); } } } i64 ans = dp[N - 1] + 1; if (ans >= INF<i64> / 2) ans = -1; print(ans); return; } int main() { INT(T); REP(T) solve(); return 0; }
感想
線形合同式を理解し直すことができて良かった。