【題解】 [NOI Online #1 入門組]魔法 最短路+矩陣快速冪+floyd+dp Luogu6189
阿新 • • 發佈:2020-10-20
Legend
Link \(\textrm{to Luogu}\)。
C 國由 \(n\) 座城市與 \(m\) 條有向道路組成,城市與道路都從 \(1\) 開始編號,經過 \(i\) 號道路需要 \(t_i\) 的費用。
現在你要從 \(1\) 號城市出發去 \(n\) 號城市,你可以施展最多 \(k\) 次魔法,使得通過下一條道路時,需要的費用變為原來的相反數,即費用從 \(t_i\) 變為 \(-t_i\)。請你算一算,你至少要花費多少費用才能完成這次旅程。
\(1 \le n \le 100,0 \le m \le \frac{n(n-1)}{2},1 \le t_i \le 10^9,0 \le k \le 10^6\)
Editorial
本題十分有意思,為出題人點贊!
首先我們求出
- \(F[0]_{i,j}\) 表示 \(i \rightarrow j\) 使用 \(0\) 次魔法的最少花費。
- \(F[1]_{i,j}\) 表示 \(i \rightarrow j\) 使用 \(1\) 次魔法的最少花費。
這個可以 Floyd
直接分層圖。
我們考慮從使用一次魔法推到使用兩次魔法:\(F[2]_{i,j}=\min\limits_{k=1}^{n}F[1]_{i,k}+F[1]_{k,j}\)。
從兩次魔法推到三次 \(F[3]_{i,j}= \min\limits_{k=1}^{n} F[2]_{i,k}+F[1]_{k,j}\)
顯然可以直接用矩陣優化這個轉移。初始矩陣 \(F[0]\),轉移矩陣 \(F[1]\)。
複雜度 \(O(n^3\log k)\)。
Code
#include <bits/stdc++.h> #define debug(...) fprintf(stderr ,__VA_ARGS__) #define LL long long using namespace std; const int MX = 200 + 2; int sz ,n ,m ,K; void chkmin(LL &a ,LL b){a = std::min(a ,b);} struct Matrix{ LL A[102][102]; Matrix operator *(const Matrix& B)const{ Matrix C; for(int i = 1 ; i <= n ; ++i){ for(int j = 1 ; j <= n ; ++j){ C.A[i][j] = 1LL << 50; for(int k = 1 ; k <= n ; ++k){ chkmin(C.A[i][j] ,A[i][k] + B.A[k][j]); // chkmin(C.A[i][j] ,B.A[i][k] + A[k][j]); } } } return C; } void output(){ for(int i = 1 ; i <= n ; ++i){ for(int j = 1 ; j <= n ; ++j){ debug("%lld%c" ,A[i][j] ," \n"[j == n]); } } debug("======================="); } }org ,tr; LL dis[MX][MX]; int main(){ cin >> n >> m >> K; memset(dis ,0x3f ,sizeof dis); for(int i = 1 ; i <= 2 * n ; ++i) dis[i][i] = 0; for(int i = 1 ,u ,v ,w ; i <= m ; ++i){ cin >> u >> v >> w; chkmin(dis[u][v] ,w); chkmin(dis[u][v + n] ,-w); chkmin(dis[u + n][v + n] ,w); } for(int k = 1 ; k <= 2 * n ; ++k) for(int i = 1 ; i <= 2 * n ; ++i) for(int j = 1 ; j <= 2 * n ; ++j) chkmin(dis[i][j] ,dis[i][k] + dis[k][j]); for(int i = 1 ; i <= n ; ++i){ for(int j = 1 ; j <= n ; ++j){ // debug("dis[%d][%d] = %lld ,mag = %lld.\n" ,i ,j ,dis[i][j] ,dis[i][j + n]); tr.A[i][j] = std::min(dis[i][j] ,dis[i][j + n]); org.A[i][j] = dis[i][j]; } } // tr.output(); while(K){ if(K & 1) org = org * tr; tr = tr * tr ,K >>= 1; } printf("%lld\n" ,org.A[1][n]); return 0; }