单源最短路正权图朴素 DijkstraΘ ( n 2 ) \Theta(n^2) Θ ( n 2 )  优化 DijkstraΘ ( m log  m ) \Theta(m\log m) Θ ( m log  m )   负权图Bellman - FordΘ ( n m ) \Theta(nm) Θ ( n m )  SPFAΘ ( n m ) \Theta(nm) Θ ( n m )    多源最短路负权图JohnsonΘ ( n m log  m ) \Theta(nm\log m) Θ ( n m log  m )  FloydΘ ( n 3 ) \Theta(n^3) Θ ( n 3 )    
Input Output 4 6 1 4 0 2 4 3 
从未确定最短路的点中选取最短路长度最小的点,为未确定最短路的点松弛出边,即d v = min  { d v ,   d u + w u v } d_v=\min\left\lbrace d_v,\ d_u+w_{uv}\right\rbrace d v  = min { d v  ,   d u  + w u v  } n n n 
时间复杂度Θ ( n 2 ) \Theta(n^2) Θ ( n 2 ) Θ ( n 2 ) \Theta(n^2) Θ ( n 2 ) 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 auto  Dijkstra = [&](int  s) {    vector<int > dis (n, inf), vis (n);     dis[s] = 0 ;       for  (int  t = 0 ; t < n; t++) {            int  u = 0 ;         for  (int  i = 0 ; i < n; i++) {             if  (!vis[i] && dis[i] < dis[u]) u = i;         }           vis[u] = 1 ;           for  (int  v = 0 ; v < n; v++) {             if  (!vis[v]) dis[v] = min (dis[v], dis[u] + E[u][v]);         }       }     return  dis; }; 
从未确定最短路的点集Q Q Q Q Q Q { d u ,   u } \left\lbrace d_u,\ u \right\rbrace { d u  ,   u } 
时间复杂度Θ ( m log  m ) \Theta(m\log m) Θ ( m log  m ) Θ ( n + m ) \Theta(n+m) Θ ( n + m ) 
没有边权的 Dijkstra(01 BFS):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 auto  bfs = [&](int  s) {    vector<int > dis (n, -1 );     dis[s] = 0 ;     queue<int > Q;     Q.push (s);     while  (!Q.empty ()) {         int  u = Q.front (); Q.pop ();         for  (auto & v : E[u]) {             if  (dis[v] != -1 ) continue ;             dis[v] = dis[u] + 1 ;             Q.push (v);         }     }     return  dis; }; 
一般的 Dijkstra:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 auto  Dijkstra = [&](int  s) {    vector vis (n, 0 );     vector dis (n, inf)  ;     dis[s] = 0 ;        priority_queue<pair<ll, int >, vector<pair<ll, int >>, greater<>> Q;     Q.push ({ 0 , s });       while  (!Q.empty ()) {         auto  [d, u] = Q.top (); Q.pop ();         if  (vis[u]) continue ;                       vis[u] = 1 ;         for  (auto  [w, v] : E[u]) {             if  (dis[v] > w + d) {                 dis[v] = w + d;                             Q.push ({ dis[v], v });                  }         }     }     return  dis; }; 
与 Dijkstra 不同,每次循环为所有点松弛出边,而不是未确定最短路的点,循环n n n n n n 
时间复杂度Θ ( n m ) \Theta(nm) Θ ( n m ) 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 auto  BellmanFord = [&](int  s) {    vector dis (n, inf);     dis[s] = 0 ;     for  (int  t = 0 ; t < n; t++) {         bool  flag = 0 ;           for  (int  u = 0 ; u < n; u++) {             for  (auto  [w, v] : E[u]) {                 if  (dis[v] > dis[u] + w) {                     dis[v] = dis[u] + w;                       flag = 1 ;                 }             }         }         if  (!flag) return  dis;       }     return  vector <ll>();   }; 
vis 优化 BellmanFord:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 auto  BellmanFord = [&](int  s) {    vector vis (n, 0 );     vector dis (n, inf)  ;     dis[s] = 0 ;     for  (int  t = 0 ; t < n; t++) {         bool  flag = 0 ;           for  (int  u = 0 ; u < n; u++) {             if  (vis[u]) continue ;             for  (auto  [w, v] : E[u]) {                 if  (dis[v] > dis[u] + w) {                     dis[v] = dis[u] + w;                       vis[v] = 0 ;                     flag = 1 ;                 }             }             vis[u] = 1 ;         }         if  (!flag) return  dis;       }     return  vector <ll>();   }; 
Queue 优化 Bellman - Ford 的 SPFA,在随机图上可实现Θ ( n log  n ) \Theta(n\log n) Θ ( n log  n ) Θ ( n m ) \Theta(nm) Θ ( n m ) 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 auto  SPFA = [&](int  n, int  s) {    vector<int > vis (n), cnt (n);     vector dis (n, inf)  ;     dis[s] = 0 , cnt[s] = 1 ;     queue<int > Q;     Q.push (s), vis[s] = 1 ;     while  (!Q.empty ()) {         int  u = Q.front (); Q.pop ();         vis[u] = 0 ;         for  (auto & [w, v] : E[u]) {             if  (dis[v] > dis[u] + w) {                 dis[v] = dis[u] + w;                 if  (!vis[v]) {                     Q.push (v), vis[v] = 1 ;                     if  (++cnt[v] > n) return  vector <ll>();                   }             }         }     }     return  dis; }; 
Input Output 5 7 0 4 11 8 11 5 5 -1 
因 Dijkstra 不能处理负权图,我们以虚拟的n + 1 n+1 n + 1 d u d_u d u  E u v : = E u v + d u − d v E_{uv} := E_{uv} + d_u - d_v E u v  : = E u v  + d u  − d v  n n n 
时间复杂度Θ ( n m log  m ) \Theta(nm\log m) Θ ( n m log  m ) 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 int  n, m;cin >> n >> m; int  N = n + 1 ;vector<vector<pii>> E (N); while  (m--) {    int  u, v, w;     cin >> u >> v >> w;     u--, v--;     E[u].push_back ({ w, v }); } int  s = n;  for  (int  i = 0 ; i < n; i++) {    E[s].push_back ({ 0 , i }); } auto  BellmanFord = [&](int  s) {    vector<bool > vis (N);     vector dis (N, inf)  ;     dis[s] = 0 ;     for  (int  t = 0 ; t < N; t++) {         bool  flag = 0 ;           for  (int  u = 0 ; u < N; u++) {             if  (vis[u]) continue ;             for  (auto  [w, v] : E[u]) {                 if  (dis[v] > dis[u] + w) {                     dis[v] = dis[u] + w;                       vis[v] = 0 ;                     flag = 1 ;                 }             }             vis[u] = 1 ;         }         if  (!flag) return  dis;       }     return  vector <ll>();   }; auto  Dijkstra = [&](int  s) {    vector vis (N, 0 );     vector dis (N, inf)  ;     dis[s] = 0 ;        priority_queue<pair<ll, int >, vector<pair<ll, int >>, greater<>> Q;     Q.push ({ 0 , s });       while  (!Q.empty ()) {         auto  [d, u] = Q.top (); Q.pop ();         if  (vis[u]) continue ;                       vis[u] = 1 ;         for  (auto  [w, v] : E[u]) {             if  (dis[v] > w + d) {                 dis[v] = w + d;                             Q.push ({ dis[v], v });                  }         }     }     return  dis; }; auto  dis = BellmanFord (s);if  (dis.empty ()) {    cout << "-1\n\n" ; } else  {     for  (int  u = 0 ; u < n; u++) {         for  (auto & [w, v] : E[u]) w += dis[u] - dis[v];       }     for  (int  u = 0 ; u < n; u++) {         auto  d = Dijkstra (u);         for  (int  v = 0 ; v < n; v++) {             if  (d[v] == inf) cout << "-1 " ;             else  cout << (d[v] -= (dis[u] - dis[v])) << ' ' ;         }         cout << '\n' ;     } } 
f k , i → j f_{k,i\to j} f k , i → j  1 ∼ k 1\sim k 1 ∼ k i i i j j j 
由d i → j = min  k { d i → k + d k → j } d_{i \to j}=\min\limits_k \left\lbrace d_{i \to k}+d_{k \to j} \right\rbrace d i → j  = k min  { d i → k  + d k → j  } f k , i → j = min  { f k − 1 , i → j ,   f k − 1 , i → k + f k − 1 , k → j } f_{k,i\to j}=\min \left\lbrace f_{k-1,i \to j},\ f_{k-1,i \to k}+f_{k-1,k \to j} \right\rbrace f k , i → j  = min { f k − 1 , i → j  ,   f k − 1 , i → k  + f k − 1 , k → j  } 
时间复杂度Θ ( n 3 ) \Theta(n^3) Θ ( n 3 ) Θ ( n 2 ) \Theta(n^2) Θ ( n 2 ) 
1 2 3 4 5 6 7 8 9 10 11 12 auto  Floyd = [&]() {    auto  dis = E;     for  (int  u = 0 ; u < n; u++) dis[u][u] = 0 ;     for  (int  k = 0 ; k < n; k++) {         for  (int  u = 0 ; u < n; u++) {             for  (int  v = 0 ; v < n; v++)                 dis[u][v] = min (dis[u][v], dis[u][k] + dis[k][v]);             if  (dis[u][u] < 0 ) return  vector<vector<ll>>();           }     }     return  dis;   } 
更新时,若边权相同,则继承父节点的最短路数量;否则替换最短路数量。最短路计数会炸 int128,可以用 double 或者取模判断。
例 1  有公路和铁路,其中铁路由点1 1 1 1 1 1 VJspr8H - Jzzhu and Cities )
思路  先用公路跑最短路,对铁路依次判断,若w ⩾ d v w\geqslant d_v w ⩾ d v  
Hacked 4 
应当用公路和铁路的所有路来跑最短路,再对铁路依次判断,若w > d v w>d_v w > d v  w = d v w=d_v w = d v  
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 int  n, m, k;cin >> n >> m >> k; vector<pair<int , int >> train; vector<vector<pair<int , int >>> E (n);   while  (m--) {    int  u, v, w;     cin >> u >> v >> w;     u--, v--;     E[u].push_back ({ w, v });     E[v].push_back ({ w, u }); } while  (k--) {    int  v, w;     cin >> v >> w;     v--;     E[0 ].push_back ({ w, v });     E[v].push_back ({ w, 0  });     train.push_back ({ w, v }); } vector dis (n, Inf)  ;    vector<int > vis (n)  ;        vector<int > from (n, -1 )  ;   vector<ll> pcnt (n)  ;        dis[0 ] = 0 , pcnt[0 ] = 1 ;    priority_queue<pair<ll, int >, vector<pair<ll, int >>, greater<>> Q; Q.push ({ 0 , 0  }); while  (!Q.empty ()) {    auto  [d, u] = Q.top (); Q.pop ();     if  (vis[u]) continue ;                   vis[u] = 1 ;     for  (auto & [w, v] : E[u]) {         if  (dis[v] > w + d) {             dis[v] = w + d;                         from[v] = u;                            pcnt[v] = pcnt[u];                      Q.push ({ dis[v], v });              }         else  if  (dis[v] == w + d) {             pcnt[v] += pcnt[u];                     if  (pcnt[v] > 1e18 ) pcnt[v] = 1e18 ;           }     } }      int  ans = 0 ;for  (auto  [w, v] : train) {    if  (w > dis[v]) ++ans;     else  if  (w == dis[v] && pcnt[v] > 1 ) --pcnt[v], ++ans; } cout << ans << "\n" ; 
例 2  无向图,有边权,m m m i i i 1 − n 1-n 1 − n n , m ⩽ 2 × 1 0 5 n ,m \leqslant 2\times 10^{5} n , m ⩽ 2 × 1 0 5 ABC375G - Road Blocked 2 )
思路  实际上是询问最短路是否必须经过u − v u-v u − v d 1 u + w + d v n = d 1 n d_{1u}+w+d_{vn}=d_{1n} d 1 u  + w + d v n  = d 1 n  p 1 u ⋅ p v n = p 1 n p_{1u}\cdot p_{vn}=p_{1n} p 1 u  ⋅ p v n  = p 1 n  p p p u u u 1 1 1 d 1 u < d 1 v d_{1u}<d_{1v} d 1 u  < d 1 v  
从1 1 1 n n n 
时间复杂度Θ ( m log  m ) \Theta(m\log m) Θ ( m log  m ) 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 int  n, m;cin >> n >> m; vector<vector<pair<int , int >>> E (n); vector<array<int , 3>> edge (m); for  (int  i = 0 ; i < m; i++) {    int  u, v, w;     cin >> u >> v >> w;     u--, v--;     E[u].push_back ({ w, v });     E[v].push_back ({ w, u });     edge[i] = { u, v, w }; } auto  Dijkstra = [&](int  s) {    vector dis (n, Inf);     vector<int > vis (n)  ;     vector<int > from (n, -1 )  ;     vector<ll> pcnt (n)  ;     dis[s] = 0 , pcnt[s] = 1 ;     priority_queue<pair<ll, int >, vector<pair<ll, int >>, greater<>> Q;     Q.push ({ 0 , s });     while  (!Q.empty ()) {         auto  [d, u] = Q.top (); Q.pop ();         if  (vis[u]) continue ;         vis[u] = 1 ;         for  (auto & [w, v] : E[u]) {             if  (dis[v] > w + d) {                 dis[v] = w + d;                 from[v] = u;                 pcnt[v] = pcnt[u];                 Q.push ({ dis[v], v });             }             else  if  (dis[v] == w + d) {                 (pcnt[v] += pcnt[u]) %= mod;             }         }     }     return  make_pair (dis, pcnt); }; auto  [d1, p1] = Dijkstra (0 );auto  [dn, pn] = Dijkstra (n - 1 );for  (auto & [u, v, w] : edge) {    if  (d1[u] > d1[v]) swap (u, v);     bool  only = (d1[u] + w + dn[v] == dn[0 ]) && (p1[u] * pn[v] % mod == pn[0 ]);     cout << (only ? "Yes"  : "No" ) << "\n" ; } 
例 1 n n n m m m k k k 1 1 1 n n n CF1307D. Cow and Fields )
思路  设在u u u v v v min  { d 1 u + 1 + d n v ,   d 1 v + 1 + d n u } \min\lbrace d_{1u}+1+d_{nv},\ d_{1v}+1+d_{nu} \rbrace min { d 1 u  + 1 + d n v  ,   d 1 v  + 1 + d n u  } 
max  1 ⩽ u , v ⩽ n { min  { d 1 u + 1 + d n v ,   d 1 v + 1 + d n u } } \max_{1 \leqslant u,v \leqslant n}\big\lbrace \min\lbrace d_{1u}+1+d_{nv},\ d_{1v}+1+d_{nu} \rbrace \big\rbrace 1 ⩽ u , v ⩽ n max  { min { d 1 u  + 1 + d n v  ,   d 1 v  + 1 + d n u  } } 
如果枚举u , v u,v u , v Θ ( k 2 ) \Theta(k^{2}) Θ ( k 2 ) 
不失一般性地假设d 1 u + 1 + d n v > d 1 v + 1 + d n u d_{1u}+1+d_{nv}> d_{1v}+1+d_{nu} d 1 u  + 1 + d n v  > d 1 v  + 1 + d n u  
max  1 ⩽ v ⩽ n ,   u ∈ S { min  { d 1 v + 1 + d n u } } = max  1 ⩽ v ⩽ n { d 1 v + 1 + min  u ∈ S { d n u } } \max_{1 \leqslant v \leqslant n,\ u\in S}\big\lbrace \min\lbrace d_{1v}+1+d_{nu} \rbrace \big\rbrace=\max_{1 \leqslant v \leqslant n}\big\lbrace d_{1v}+1+\min_{u\in S}\lbrace d_{nu} \rbrace \big\rbrace 1 ⩽ v ⩽ n ,   u ∈ S max  { min { d 1 v  + 1 + d n u  } } = 1 ⩽ v ⩽ n max  { d 1 v  + 1 + u ∈ S min  { d n u  } } 
式中,u u u d 1 u + 1 + d n v > d 1 v + 1 + d n u d_{1u}+1+d_{nv}> d_{1v}+1+d_{nu} d 1 u  + 1 + d n v  > d 1 v  + 1 + d n u  d 1 u − d n u > d 1 v − d n v d_{1u}-d_{nu}> d_{1v}-d_{nv} d 1 u  − d n u  > d 1 v  − d n v  d 1 u − d n u d_{1u}-d_{nu} d 1 u  − d n u  v v v u u u v v v min  u ∈ S { d n u } \min\limits_{u\in S}\lbrace d_{nu}\rbrace u ∈ S min  { d n u  } d v u d_{vu} d v u  Θ ( k ) \Theta(k) Θ ( k ) 
时间复杂度Θ ( n + k log  k ) \Theta(n+k\log k) Θ ( n + k log  k ) 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 void  eachT ()      int  n, m, k;     cin >> n >> m >> k;     vector<int > tag (k)  ;     for  (int  i = 0 ; i < k; i++) {         cin >> tag[i];         tag[i]--;     }     vector<vector<pii>> E (n);     while  (m--) {         int  u, v;         cin >> u >> v;         u--, v--;         E[u].push_back ({ 1 , v });         E[v].push_back ({ 1 , u });     }     auto  Dijkstra = [&](int  st) {         vector dis (n, Inf);         dis[st] = 0 ;         vector<int > vis (n)  ;         using  T = pair<ll, int >;         priority_queue<T, vector<T>, greater<>> Q;         Q.push ({ 0 , st });         while  (!Q.empty ()) {             auto  [d, u] = Q.top (); Q.pop ();             if  (vis[u]) continue ; vis[u] = 1 ;             for  (auto & [w, v] : E[u]) {                 if  (dis[v] > w + d) {                     dis[v] = w + d;                     Q.push ({ dis[v], v });                 }             }         }         return  dis;     };     auto  d0 = Dijkstra (0 );     auto  dn = Dijkstra (n - 1 );     using  node = struct  { ll d0, dn; };     vector<node> dp (k)  ;     for  (int  i = 0 ; i < k; i++) {         dp[i].d0 = d0[tag[i]];         dp[i].dn = dn[tag[i]];     }     sort (dp.begin (), dp.end (), [](node a, node b) {         return  a.d0 - a.dn > b.d0 - b.dn;     });     ll ans = 0 , dnmx = dp[0 ].dn;     for  (int  i = 1 ; i < k; i++) {         ans = max (ans, dp[i].d0 + dnmx + 1 );         dnmx = max (dnmx, dp[i].dn);     }     ans = min (ans, dn[0 ]);     cout << ans << '\n' ; } 
分层图用于解决 边权改变  的最短路问题。层数k k k k n kn k n k ⩽ 10 k \leqslant 10 k ⩽ 1 0 
第i i i u u u i n + u in+u i n + u f f f t t t 
1 2 3 4 5 6 int  N = n * k;vector<vector<pii>> E (N); auto  add = [&](int  f, int  t, int  u, int  v, int  w) {    E[f * n + u].push_back ({ w, t * n + v });     E[f * n + v].push_back ({ w, t * n + u }); }; 
例 1  给定n n n m m m k k k 洛谷 P4568 飞行路线 )
数据范围:2 ⩽ n ⩽ 1 0 4 2 \leqslant n \leqslant 10^4 2 ⩽ n ⩽ 1 0 4 1 ⩽ m ⩽ 5 × 1 0 4 1 \leqslant m \leqslant 5\times 10^4 1 ⩽ m ⩽ 5 × 1 0 4 0 ⩽ k ⩽ 10 0 \leqslant k \leqslant 10 0 ⩽ k ⩽ 1 0 0 ⩽ w ⩽ 1 0 3 0 \leqslant w \leqslant 10^3 0 ⩽ w ⩽ 1 0 3 
思路  建k + 1 k+1 k + 1 0 0 0 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 void  eachT ()      int  n, m, k, st, ed;     cin >> n >> m >> k >> st >> ed;     ++k;     int  N = n * k;     vector<vector<pii>> E (N);     auto  add = [&](int  f, int  t, int  u, int  v, int  w) {         E[f * n + u].push_back ({ w, t * n + v });         E[f * n + v].push_back ({ w, t * n + u });     };     while  (m--) {         int  u, v, w;         cin >> u >> v >> w;         for  (int  i = 0 ; i < k; i++) add (i, i, u, v, w);         for  (int  i = 1 ; i < k; i++) add (i - 1 , i, u, v, 0 );     }     auto  dis = Dijkstra (st);     int  ans = inf;     for  (int  i = 0 ; i < k; i++) {         ans = min (ans, dis[i * n + ed]);     }     cout << (ans == Inf ? -1  : ans) << '\n' ; } 
例 2  给定一个包含n n n m m m E E E ∑ w i − max  w i + min  w i \sum w_i-\max w_i+\min w_i ∑ w i  − max w i  + min w i  1 1 1 
数据范围:2 ⩽ n ⩽ 2 × 1 0 5 ,   1 ⩽ m ⩽ 2 × 1 0 5 ,   0 ⩽ w i ⩽ 1 0 9 2 \leqslant n \leqslant 2\times10^5,\ 1 \leqslant m \leqslant 2\times10^5,\ 0 \leqslant w_i \leqslant 10^9 2 ⩽ n ⩽ 2 × 1 0 5 ,   1 ⩽ m ⩽ 2 × 1 0 5 ,   0 ⩽ w i  ⩽ 1 0 9 
思路  可以把路径的权值理解为:必须将路径上的任意一条边不算代价,任意一条边算 2 倍代价。这样我们就把对两条极值边的贡献方式转化成了所有边的贡献方式。更加统一,易于处理。
这两种特殊代价没有先后顺序,故建4 4 4 1 → 2 1\to2 1 → 2 0 0 0 2 → 4 2\to4 2 → 4 2 w 2w 2 w 1 → 3 1\to3 1 → 3 2 w 2w 2 w 3 → 4 3\to4 3 → 4 0 0 0 
答案为第1 1 1 4 4 4 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 void  eachT ()      int  n, m;     cin >> n >> m;     int  N = n * 4 ;     vector<vector<pii>> E (N);     auto  add = [&](int  f, int  t, int  u, int  v, int  w) {         E[f * n + u].push_back ({ w, t * n + v });         E[f * n + v].push_back ({ w, t * n + u });     };     while  (m--) {         int  u, v, w;         cin >> u >> v >> w;         u--, v--;         for  (int  i = 0 ; i < 4 ; i++) add (i, i, u, v, w);         add (0 , 1 , v, u, 0 );         add (1 , 3 , v, u, 2  * w);         add (0 , 2 , v, u, 2  * w);         add (2 , 3 , v, u, 0 );     }     auto  dis = Dijkstra (0 );     for  (int  i = 1 ; i < n; i++) {         cout << min (dis[i], dis[3  * n + i]) << ' ' ;     } } 
例 3  骑马 CF2014E. ZRendez-vous de Marian et Robin 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 void  eachT ()      int  n, m, h;     cin >> n >> m >> h;     vector<int > ishalf (n)  ;     for  (int  i = 0 ; i < h; i++) {         int  x;         cin >> x;         x--;         ishalf[x] = 1 ;     }     vector<vector<pii>> E (n);     while  (m--) {         int  u, v, w;         cin >> u >> v >> w;         u--, v--;         E[u].push_back ({ w, v });         E[v].push_back ({ w, u });     }     auto  Dijkstra = [&](int  st) {         vector dis (n, array{ Inf, Inf });         dis[st][0 ] = 0 ;         vector vis (n, array{ 0 , 0  })  ;         using  T = tuple<ll, int , bool >;         priority_queue<T, vector<T>, greater<>> Q;         Q.push ({ 0 , st, 0  });           while  (!Q.empty ()) {             auto  [d, u, half] = Q.top (); Q.pop ();             if  (vis[u][half]) continue ; vis[u][half] = 1 ;             if  (ishalf[u]) half = 1 ;             for  (auto  [w, v] : E[u]) {                 if  (half) w /= 2 ;                 if  (dis[v][half] > d + w) {                     dis[v][half] = d + w;                     Q.push ({ dis[v][half], v, half });                 }             }         }         vector<ll> d (n)  ;         for  (int  i = 0 ; i < n; i++) {             d[i] = min (dis[i][0 ], dis[i][1 ]);         }         return  d;     };          auto  d0 = Dijkstra (0 );     auto  dn = Dijkstra (n - 1 );     ll ans = Inf;     for  (int  i = 0 ; i < n; i++) {         ans = min (ans, max (d0[i], dn[i]));     }     cout << (ans == Inf ? -1  : ans) << '\n' ; } 
例 4  2024 ICPC 网络赛 II E. Escape 
机器人的活动范围距离起点的距离最大是d d d 
对于某个点,玩家到达步数是x x x y y y x x x y y y x ⩾ y x \geqslant y x ⩾ y x , y x,y x , y x x x y y y y y y y y y 
用 BFS 寻路。寻路的时候记录步数x x x y y y 
每个点都可以经过两次,所以 vis 等数组都开成二维的,奇偶相互独立。
注意输出路径时不能写 if (u == 1) break,可能玩家走 1->2->3->1 改变奇偶性,导致实际路径长度与输出的不符。在 BFS 里走到n n n 
Bonus:如果数据有d = 0 d=0 d = 0 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 int  n, m, D;cin >> n >> m >> D; vector<vector<int >> E (n); while  (m--) {    int  u, v;     cin >> u >> v;     u--, v--;     E[u].push_back (v);     E[v].push_back (u); } queue<pair<int , int >> Q; vector val (n, array{ inf, inf })  ;int  k;cin >> k; while  (k--) {    int  x;     cin >> x;     x--;     val[x][0 ] = 0 ;     Q.push ({ 0 , x }); } while  (!Q.empty ()) {    auto  [d, u] = Q.front ();     Q.pop ();     if  (d >= D) continue ;     ++d;     for  (auto & v : E[u]) {         if  (val[v][d & 1 ] != inf) continue ;         val[v][d & 1 ] = d;         Q.push ({ d, v });     } } vector from (n, array{ -1 , -1  })  ;Q.push ({ 0 , 0  }); while  (!Q.empty ()) {    auto  [d, u] = Q.front ();     Q.pop ();     if  (u == n - 1 ) {         cout << d << '\n' ;         vector<int > path;         while  (d >= 0 ) {             path.push_back (u);             u = from[u][d & 1 ];             d--;         }         for  (int  i = path.size () - 1 ; i >= 0 ; --i) {             cout << path[i] + 1  << ' ' ;         }         cout << '\n' ;         return  0 ;     }     ++d;     for  (auto & v : E[u]) {         if  (d >= val[v][d & 1 ]) continue ;         if  (~from[v][d & 1 ]) continue ;         from[v][d & 1 ] = u;         Q.push ({ d, v });     } } cout << "-1\n" ; 
例 5  Alice 和 Bob 在一张简单无向图上博弈,Alice 先手。每次每人必须沿着当前所在的点的一条边走到另一端,不能走到对手所在的位置,不能行动者输。对于每个i i i 1 1 1 n − 1 n-1 n − 1 0 0 0 i i i SunBoYi  [[…/contests/2024 杭电多校 /2024-08-18:2024“钉耙编程”中国大学生算法设计超级联赛(10)|2024-08-18:2024“钉耙编程”中国大学生算法设计超级联赛(10)]])
首先特判两人在两个连通块里的情况,是简单的,除了孤立点的情况外,必然可以一直沿着一条边反复横跳无限走。接下来默认两人在同一连通块。
不难发现,如果一个人走到了环上,那么他就立于不败之地。同样不难发现,如果一根人走到了连接两个环的一根链上,他也不会输,因为其两边必然只能有最多一边可能被对手阻挡,那么他一定能走到一个环上。我们称这两类点为好点。
所以我们对于两人分别求出,自己能否阻挡对方进入某个好点。
首先先判断掉该连通块是树的情况,此时不存在好点,胜负只与初始 Alice 和 Bob 的距离的奇偶性有关。
否则,我们先求出好点,用类似基环树和拓扑排序的方法剥一度点,剩下的都是好点。
对于一个人,考虑为了阻挡对手进入好点,他必须尽快赶到离对手最近的好点。同时,与树的情况类似的,还需要保持距离的奇偶性正确,才能把对手逼到某个一度点,不然他会被迫后退,让对手走到好点。所以相当于要求他的初始位置到离对手最近的好点,且奇偶性给定的最短路长度,并于对手要走的距离进行比较。由于起点或终点固定,故可以分层图 BFS 解决。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 int  n, m;cin >> n >> m; DSU dsu (n)  ;vector<vector<int >> E (n); while  (m--) {    int  u, v;     cin >> u >> v;     u--, v--;     E[u].push_back (v);     E[v].push_back (u);     dsu.uno (u, v); } string ans (n - 1 , '?' )  ;for  (int  i = 1 ; i < n; i++) {    if  (!dsu.same (i, 0 )) {         if  (dsu.size (0 ) == 1 ) ans[i] = 'B' ;         else  if  (dsu.size (i) == 1 ) ans[i] = 'A' ;         else  ans[i] = 'D' ;     } } vector<int > dis (n) , vis (n)  ;auto  dfs = [&](this  auto && self, int  u, int  p) {    vis[u] = true ;     for  (auto & v : E[u]) {         if  (v == p) continue ;         if  (vis[v]) return  false ;           dis[v] = dis[u] + 1 ;         if  (!self (v, u)) return  false ;     }     return  true ;   }; if  (dfs (0 , -1 )) {      for  (int  i = 1 ; i < n; i++) {         if  (dsu.same (i, 0 )) ans[i] = dis[i] & 1  ? 'B'  : 'A' ;     } } else  {      vector<int > deg (n);     SSN ssn (n)  ;     queue<int > Q;     for  (int  i = 0 ; i < n; i++) {         if  (dsu.same (i, 0 )) {             deg[i] = E[i].size ();             if  (deg[i] == 1 ) Q.push (i);         }     }     while  (!Q.empty ()) {         int  u = Q.front (); Q.pop ();         for  (auto & v : E[u]) {             ssn.uno (v, u, 1 );               if  (--deg[v] == 1 ) Q.push (v);         }     }     auto  bfs = [&](int  st) {         vector dis (n, array{ inf, inf });         queue<pair<int , int >> Q;         dis[st][0 ] = 0 ;         Q.push ({ st, 0  });         while  (Q.size ()) {             auto  [u, d] = Q.front (); Q.pop ();             for  (auto & v : E[u]) {                 if  (dis[v][!d] != inf) continue ;                 dis[v][!d] = dis[u][d] + 1 ;                 Q.push ({ v, !d });             }         }         return  dis;     };     auto  d0 = bfs (0 );     auto  dp = bfs (ssn.find (0 ));     for  (int  i = 1 ; i < n; i++) {         if  (dsu.same (i, 0 )) {             if  (d0[ssn.find (i)][ssn.depth (i) & 1 ] <= ssn.depth (i)) ans[i] = 'A' ;             else  if  (dp[i][!(ssn.depth (0 ) & 1 )] <= ssn.depth (0 ) - 1 ) ans[i] = 'B' ;             else  ans[i] = 'D' ;         }     } } for  (int  i = 1 ; i < n; i++) {    cout << ans[i]; } cout << "\n" ; 
坐地铁,求最少搭乘几种线路,即求最小地铁换乘次数 + 1。(2260, CF1941G. Rudolf and Subway )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int  n, m;cin >> n >> m; vector<vector<int >> E (n + m); while  (m--) {    cin >> u >> v >> c;     u--, v--, c += n;     E[c].push_back (u), E[u].push_back (c);     E[c].push_back (v), E[v].push_back (c); }   int  s, t;cin >> s >> t; s--, t--; cout << (bfs (s)[t] / 2 ) << "\n" ; 
题意  有向图,每条边的边权为 1km,走路的速度为每单位时间 1km,空间跑路器为每单位时间2 k 2^k 2 k k k k 1 1 1 n n n 2 ⩽ n ⩽ 50 2 \leqslant n \leqslant 50 2 ⩽ n ⩽ 5 0 m ⩽ 1 0 4 m \leqslant 10 ^ 4 m ⩽ 1 0 4 VJsum4A - 洛谷 P1613 跑路 )
思路  如果有u , v u,v u , v 2 k 2^k 2 k f i , j , k f_{i,j,k} f i , j , k  2 i 2^i 2 i j j j k k k 
f i , v , u ← { f i − 1 , v , k ⊗ f i − 1 , k , u , i > 0 E u , v , i = 0 f_{i, v, u}\leftarrow \begin{cases} f_{i-1, v, k} \otimes f_{i-1, k, u},& i>0\\ E_{u,v},& i=0 \end{cases} f i , v , u  ← { f i − 1 , v , k  ⊗ f i − 1 , k , u  , E u , v  ,  i > 0 i = 0  
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 int  n, m;cin >> n >> m; vector f (D, vector(n, vector<int >(n)))  ;while  (m--) {    int  u, v;     cin >> u >> v;     u--, v--;     f[0 ][u][v] = 1 ; } for  (int  bit = 1 ; bit < D; bit++) {    for  (int  k = 0 ; k < n; ++k)         for  (int  u = 0 ; u < n; u++)             for  (int  v = 0 ; v < n; v++)                 f[bit][u][v] |= f[bit - 1 ][u][k] & f[bit - 1 ][k][v]; } vector adj (n, vector(n, inf))  ;for  (int  u = 0 ; u < n; u++) {    for  (int  v = 0 ; v < n; v++)         for  (int  bit = 0 ; bit < D; bit++)             if  (f[bit][u][v]) adj[u][v] = 1 ; } for  (int  k = 0 ; k < n; ++k) {    for  (int  u = 0 ; u < n; u++)         for  (int  v = 0 ; v < n; v++)             adj[u][v] = min (adj[u][v], adj[u][k] + adj[k][v]); } cout << adj[0 ][n - 1 ] << "\n" ; 
给定无向图,求从起点到终点的恰好经过k k k m ⩽ 100 m \leqslant 100 m ⩽ 1 0 0 2 ⩽ k ⩽ 1 0 6 2 \leqslant k \leqslant 10^{6} 2 ⩽ k ⩽ 1 0 6 AcWing345. 牛站  , 洛谷 P2886 Cow Relays G )
设f i , u , v f_{i,u,v} f i , u , v  i i i 
f i , u , v ← { f i − 1 , u , k + E k , v , i > 1 E u , v , i = 1 f_{i, u, v}\leftarrow \begin{cases} f_{i-1, u, k} +E_{k,v},& i>1\\ E_{u, v},& i=1 \end{cases} f i , u , v  ← { f i − 1 , u , k  + E k , v  , E u , v  ,  i > 1 i = 1  
时间复杂度为Θ ( n 3 k ) \Theta(n^{3}k) Θ ( n 3 k ) f i , u , v f_{i,u,v} f i , u , v  Θ ( n 3 log  k ) \Theta(n^{3}\log k) Θ ( n 3 log  k ) 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 using  Matrix = vector<vector<int >>;Matrix mul (Matrix& a, Matrix& b)   {    const  int  n = a.size ();     Matrix res (n, vector<int >(n, inf))  ;          for  (int  k = 0 ; k < n; ++k) {         for  (int  u = 0 ; u < n; u++) {             for  (int  v = 0 ; v < n; v++) {                 res[u][v] = min (res[u][v], a[u][k] + b[k][v]);             }         }     }     return  res; } Matrix qpow (Matrix& a, int  n)   {    auto  res = a;     --n;     while  (n) {         if  (n & 1 ) res = mul (res, a);         a = mul (a, a);         n >>= 1 ;     }     return  res; } int  main ()      int  k, m;     cin >> k >> m;     int  n = 0 ;     unordered_map<int , int > mp;     auto  get = [&](int  u) {         if  (!mp.count (u)) mp[u] = n++;         return  mp[u];     };     int  s, t;     cin >> s >> t;     s = get (s), t = get (t);     vector adj (m, vector(m, inf))  ;     while  (m--) {         int  u, v, w;         cin >> w >> u >> v;         u = get (u), v = get (v);         adj[u][v] = adj[v][u] = min (adj[u][v], w);     }     auto  res = qpow (adj, k);     cout << res[s][t] << "\n" ;     return  0 ; } 
时间复杂度Θ ( k m ) \Theta(km) Θ ( k m ) 
1 2 3 4 5 6 7 8 9 10 11 vector dis (n, inf)  ;dis[s] = 0 ; for  (int  t = 0 ; t < k; t++) {    vector ndis (n, inf)  ;        for  (auto & [w, u, v] : E) {         ndis[u] = min (ndis[u], dis[v] + w);         ndis[v] = min (ndis[v], dis[u] + w);     }     dis = ndis; } cout << dis[t] << endl; 
一种看不懂的做法:
当N N N 
性质 1 :只有可能在路径上一条最短的边上连续走多次。
证明 :假如在其他边上连续走了多次,那我们可以将路径上来回走的一对边( u → v , v → u ) (u\to v,v\to u) ( u → v , v → u ) 
性质 2 :只有路径上的一条最短边可能被在同一方向经过三次及以上,这里“经过”不要求连续。
证明 :假如另外一条边被在同一方向经过三次及以上,那么路径上一定有包含这条边的两个环。若这两个环中有至少一个长度为偶数,那么可以删去这个环并在最短边上来回走;否则,两个环的长度都是奇数,把两个环都删去,换成在最短边上来回走即可。
结论 :最优方案下,只会有至多一条边被经过> 2 >2 > 2 
设f u , d f_{u,d} f u , d  S S S u u u d d d ≤ 2 \le 2 ≤ 2 d ≤ 2 T d\le 2T d ≤ 2 T f f f O ( T 2 ) \mathcal{O}(T^2) O ( T 2 ) g u , d g_{u,d} g u , d  E E E 
枚举每条边作为那条被多次经过的边,并O ( T ) \mathcal{O}(T) O ( T ) Calc 函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 #include  <iostream>  #include  <algorithm>  #include  <vector>  #include  <unordered_map>  using  namespace  std;typedef  long  long  ll;const  int  inf = 0x3f3f3f3f ;signed  main ()      int  k, m;     cin >> k >> m;     int  n = 0 ;     unordered_map<int , int > mp;     auto  get = [&](int  u) {         if  (!mp.count (u)) mp[u] = n++;         return  mp[u];     };     int  s, t;     cin >> s >> t;     s = get (s), t = get (t);     vector<tuple<int , int , int >> E (m);     for  (auto & [w, u, v] : E) {         cin >> w >> u >> v;         u = get (u), v = get (v);     }     auto  bfs = [&](int  s) {         vector dis (m, vector (m + 1  << 1 , inf));         dis[s][0 ] = 0 ;         for  (int  i = 1 ; i <= (m << 1  | 1 ); i++) {             for  (auto & [w, u, v] : E) {                 dis[u][i] = min (dis[u][i], dis[v][i - 1 ] + w);                 dis[v][i] = min (dis[v][i], dis[u][i - 1 ] + w);             }         }         return  dis;     };     auto  ds = bfs (s), dt = bfs (t);     auto  Calc = [&](int  u, int  w) {         int  res = inf;         for  (int  c = 0 ; c <= 1 ; c++) {             int  mn = inf;             for  (int  j = m * 2  + c, l = (k + c) % 2  - 2 ; j >= 0 ; j -= 2 ) {                 while  (l < m * 2  && k - j - l>1 ) {                     l += 2 ;                     mn = min (mn + 2  * w, dt[u][l]);                 }                 if  (mn >= inf || ds[u][j] >= inf) continue ;                 res = min (res, mn + ds[u][j] + (k - j - l) * w);             }         }         return  res;     };     int  ans = inf;     for  (auto & [w, u, v] : E) {         ans = min (ans, min (Calc (u, w), Calc (v, w)));     }     cout << ans << endl;     return  0 ; } 
例 1  小红拿到了一个数组,她可以进行若干次以下操作:
选择一个元素,花费p p p x = 1 x=1 x = 1  选择一个元素,花费q q q y y y  希望若干次操作后,数组的平均数是一个整数,求最小总代价。(小红的数组操作 )
方法一  直接算,注意可以减为负数,所以循环从1 1 1 n n n 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int  n, p, x, q, y;cin >> n >> p >> x >> q >> y; int  sum = 0 ;for  (int  i = 0 ; i < n; i++) {    int  x;     cin >> x;     (sum += x) %= n; } ll min = 1e18 ; for  (int  i = 0 ; i < n; i++) {    min = std::min (min, 1ll  * q * i + p * (n - (sum - 1ll  * i * y - 1 ) % n - 1 )); } cout << min << "\n" ; 
这种方法在 Hard 版本仍然使用,不同的是,我们需要知道每次加x x x k k k 
a x ≡ b ( m o d n ) ax≡b \pmod n a x ≡ b ( m o d n ) 开个数组直接预处理出k x kx k x n n n  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int  n, p, x, q, y;cin >> n >> p >> x >> q >> y; int  sum = 0 ;for  (int  i = 0 ; i < n; i++) {    int  x;     cin >> x;     (sum += x) %= n; } vector mp (n, Inf)  ;for  (int  i = n - 1 ; i >= 0 ; i--) {      mp[(1ll  * i * x + sum) % n] = 1ll  * i * p; } ll res = Inf; for  (int  i = 0 ; i <= n; i++) {    res = min (res, mp[1ll  * i * y % n] + 1ll  * i * q); } cout << (res == Inf ? -1  : res); 
方法二  求最小代价,可以联想到最短路:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 int  n, p, x, q, y;cin >> n >> p >> x >> q >> y; int  s = 0 ;for  (int  i = 0 ; i < n; i++) {    int  x;     cin >> x;     (s += x) %= n; } vector dis (n, Inf)  ;vector<bool > vis (n)  ;priority_queue<pair<ll, int >, vector<pair<ll, int >>, greater<>> Q; Q.push ({ 0 , s }); dis[s] = 0 ; while  (!Q.empty ()) {    auto  [d, u] = Q.top ();     Q.pop ();     if  (vis[u]) continue ;     vis[u] = 1 ;          int  v1 = ((u + x) % n + n) % n;     if  (dis[v1] > dis[u] + p) {         dis[v1] = dis[u] + p;         Q.push ({ dis[v1], v1 });     }          int  v2 = ((u - y) % n + n) % n;     if  (dis[v2] > dis[u] + q) {         dis[v2] = dis[u] + q;         Q.push ({ dis[v2], v2 });     } } cout << (dis[0 ] == Inf ? -1  : dis[0 ]) << endl; 
例 2 n n n 1 1 1 n n n a i a_i a i  b i b_i b i  
从第一个问题开始回答,按如下次序回答每个问题,两种选择:
提交问题并获得a i a_i a i  j j j j < i j<i j < i  跳过问题,然后问题作废,下一题的下标j j j j ⩽ b i j \leqslant b_{i} j ⩽ b i   最大化得分。(CF2023B. Skipping )
这个「下一题」类似于图论中的有向边,最大化得分即是最长路,考虑建图。
i → b i i\to b_{i} i → b i  − a i -a_{i} − a i  i → i − 1 i\to i-1 i → i − 1 0 0 0 i → t i\to t i → t ∑ j = 1 i a j \sum_{j=1}^{i} a_{j} ∑ j = 1 i  a j  t t t 跑1 → t 1\to t 1 → t 3 n 3n 3 n Θ ( n log  n ) \Theta(n\log n) Θ ( n log  n ) 
例 3  Tenzing 有n n n 
1 1 1 n n n 有k k k ( x , y , z ) (x, y, z) ( x , y , z ) x x x y y y z z z  最大化聚会总时间,并给出相应方案,即给出总聚会时间,每次聚会给出参与聚会的人和单次聚会时长。n ⩽ 2 × 1 0 5 n \leqslant 2\times 10^{5} n ⩽ 2 × 1 0 5 CF1842D. Tenzing and His Animal Friends )
矛盾冲突点在n n n u − v u-v u − v u u u v v v 1 1 1 n n n 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 int  n, m;cin >> n >> m; vector<vector<pair<int , int >>> E (n); while  (m--) {    int  u, v, w;     cin >> u >> v >> w;     u--, v--;     E[u].push_back ({ w, v });     E[v].push_back ({ w, u }); }   auto  dis = Dijkstra (0 );if  (dis[n - 1 ] == Inf) {    cout << "inf\n" ; } else  {     vector<int > pos (n);     iota (pos.begin (), pos.end (), 0 );     sort (pos.begin (), pos.end (), [&](const  int & a, const  int & b) {         return  dis[a] == dis[b] ? a < b : dis[a] < dis[b];       });     vector<pair<string, ll>> res;     string s (n, '0' )  ;     for  (int  i = 0 ; i < n; i++) {         if  (pos[i] == n - 1 ) break ;           s[pos[i]] = '1' ;         res.push_back ({ s, dis[pos[i + 1 ]] - dis[pos[i]] });     }     cout << dis[n - 1 ] << " "  << res.size () << "\n" ;     for  (auto & [s, d] : res) {         cout << s << " "  << d << "\n" ;     } } 
题意  无向图,有边权,两种操作:
永久删去一条边; 询问u , v u,v u , v  数据范围:n ⩽ 300 ,   q ⩽ 2 × 1 0 5 ,   q 1 ⩽ 300 n \leqslant 300,\ q \leqslant 2\times 10^{5}, \ q_{1} \leqslant 300 n ⩽ 3 0 0 ,   q ⩽ 2 × 1 0 5 ,   q 1  ⩽ 3 0 0 ABC375F - Road Blocked )
思路  对于操作一,跑 300 次全源最短路时间是不够的,而且这加强了问题。应当考虑删边的影响。
删边不好做,倒着处理询问,转化为加边。设加上了s − t s-t s − t u − v u-v u − v s − t s-t s − t d u − s + w + d t − v d_{u-s}+w+d_{t-v} d u − s  + w + d t − v  d u − t + w + d s − v d_{u-t}+w+d_{s-v} d u − t  + w + d s − v  u , v u,v u , v Θ ( n 3 + q 1 n 2 ) \Theta(n^{3}+q_{1}n^{2}) Θ ( n 3 + q 1  n 2 ) 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 int  n, m, q;cin >> n >> m >> q; vector<array<int , 3>> E (m); for  (int  i = 0 ; i < m; i++) {    int  u, v, w;     cin >> u >> v >> w;     u--, v--;     E[i] = { u, v, w }; } vector<array<int , 3>> que (q); vector<int > del (m)  ;for  (int  i = 0 ; i < q; i++) {    int  op;     cin >> op;     if  (op == 1 ) {         int  x;         cin >> x;         x--;         del[x] = 1 ;         que[i] = { op, x, 0  };     } else  {         int  u, v;         cin >> u >> v;         u--, v--;         que[i] = { op, u, v };     } } vector dis (n, vector(n, Inf))  ;for  (int  u = 0 ; u < n; u++) {    dis[u][u] = 0 ; } for  (int  i = 0 ; i < m; i++) {    if  (!del[i]) {         auto  [u, v, w] = E[i];         dis[u][v] = dis[v][u] = w;     } } for  (int  k = 0 ; k < n; ++k) {    for  (int  u = 0 ; u < n; u++) {         for  (int  v = 0 ; v < n; v++) {             dis[u][v] = min (dis[u][v], dis[u][k] + dis[k][v]);         }     } } vector<ll> ans (q)  ;for  (int  i = q - 1 ; i >= 0 ; i--) {    auto  [op, a, b] = que[i];     if  (op == 1 ) {         auto  [s, t, w] = E[a];         for  (int  u = 0 ; u < n; u++) {             for  (int  v = 0 ; v < n; v++)                 dis[u][v] = min (dis[u][v], min (dis[u][s] + dis[t][v] + w, dis[u][t] + dis[s][v] + w));         }     } else  {         ans[i] = dis[a][b];     } } for  (int  i = 0 ; i < q; i++) {    if  (ans[i]) cout << (ans[i] == Inf ? -1  : ans[i]) << "\n" ; } 
给出一张每个顶点v v v c v c_v c v  w v w_v w v  v v v w v w_v w v  
你有一个棋子, 棋子只能放在 / 移动到被激活的顶点上。你可以随时选择一种颜色c c c c c c 
现在对于图中的每一对顶点( s , t ) (s, t) ( s , t ) s s s t t t 
数据范围:n ⩽ 300 n \leqslant 300 n ⩽ 3 0 0 
思路  考虑答案形态。
注意到每走到一个不同颜色的顶点上时你都可以回收其他颜色的所有顶点的点权。将答案(一条路径,视为顶点序列) 按颜色分段,则其所需的最小初始点权为“每一段的点权和加上下一段的第一个顶点的点权”的最大值。
于是我们可以的将图按颜色分为多个同色连通块,每块内部用 Floyd-Warshall 求两两之间最小点权路径。
然后通过枚举每段的起点,终点,以及下一段的起点建出一 张带边权的有向图,边权即为连接起点与终点的最小点权路径的点权之和加上下一段起点的点权。
最后在这张有向图上从每个顶点出发使用类似 Prim 的做法(每轮选择一个未连通的点权最小的出点)找出以每个点 s 为根的有向瓶颈生成树。
时间复杂度Θ ( n 3 ) \Theta(n^{3}) Θ ( n 3 ) 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 #include  <iostream>  #include  <vector>  using  namespace  std;using  ll = long  long ;const  ll Inf = 0x3f3f3f3f3f3f3f3f ;signed  main ()      ios::sync_with_stdio (false );     cin.tie (nullptr );     cout.tie (nullptr );     int  t;     cin >> t;     while  (t--) {         int  n, m;         cin >> n >> m;         vector<int > c (n) , w (n)  ;         for  (int  i = 0 ; i < n; i++) {             cin >> c[i];         }         for  (int  i = 0 ; i < n; i++) {             cin >> w[i];         }         vector adj (n, vector<int >(n))  ;         vector dis (n, vector<ll>(n, Inf))  ;         while  (m--) {             int  u, v;             cin >> u >> v;             u--, v--;             adj[u][v] = adj[v][u] = 1 ;             if  (c[u] == c[v]) {                 dis[u][v] = dis[v][u] = w[u] + w[v];             }         }         for  (int  u = 0 ; u < n; u++) {             dis[u][u] = w[u];         }                  for  (int  k = 0 ; k < n; ++k) {             for  (int  u = 0 ; u < n; u++) {                 if  (c[u] != c[k]) continue ;                 for  (int  v = 0 ; v < n; v++) {                     if  (c[v] != c[u]) continue ;                     dis[u][v] = min (dis[u][v], dis[u][k] + dis[k][v] - w[k]);                     dis[v][u] = min (dis[v][u], dis[v][k] + dis[k][u] - w[k]);                 }             }         }                  for  (int  u = 0 ; u < n; u++) {             for  (int  v = 0 ; v < n; v++) {                 if  (c[u] != c[v]) continue ;                 for  (int  s = 0 ; s < n; ++s) {                     if  (c[v] != c[s] && adj[v][s])                         dis[u][s] = min (dis[u][s], dis[u][v] + w[s]);                 }             }         }         for  (int  s = 0 ; s < n; ++s) {                          vector<ll> mn (n, Inf) , dp (n, Inf) , ndp (n, Inf)  ;             vector<int > vis (n)  ;             dp[s] = mn[s] = 0 ;             for  (int  t = 0 ; t < n; t++) {                 int  u = -1 ;                 for  (int  j = 0 ; j < n; j++) {                     if  (!vis[j] && (u == -1  || mn[j] < mn[u])) {                         u = j;                     }                 }                 if  (u == -1 ) break ;                 vis[u] = 1 ;                 for  (int  v = 0 ; v < n; v++) {                     if  (!vis[v] && c[u] != c[v] && dis[u][v] < mn[v]) {                         mn[v] = dis[u][v];                         dp[v] = max (dp[u], mn[v]);                       }                 }             }                          for  (int  u = 0 ; u < n; u++) {                 for  (int  v = 0 ; v < n; v++) {                     if  (c[u] == c[v]) ndp[v] = min (ndp[v], max (dp[u], dis[u][v]));                 }             }             for  (int  u = 0 ; u < n; u++) {                 dp[u] = min (dp[u], ndp[u]);             }                          for  (int  u = 0 ; u < n; u++) {                 cout << dp[u] << " \n" [u == n - 1 ];             }         }     }          return  0 ; }