題目
分析
表示 到 用 次魔法的最小代價, 表示 到 用一次魔法的最小代價:
枚舉的中轉節點 恰似 Floyd 算法,於是套路矩陣加速即可, 表示不用魔法 到 最小代價,則答案爲 其中 號不是常規矩陣乘法,而與 DP 式相似,由於 有對 的分配率(),所以這個矩陣運算有結合律,所以可以快速冪。
錯因
- DP 式列錯,想的是轉移的最後一條邊用魔法,死活快速冪做不出來,實際上是最後一段的某一條邊用魔法;
- 分層圖騙分還沒有連 代價爲 的邊,70 pts 50 pts。
代碼
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <vector>
typedef long long LL;
const int MAXN = 100;
const int MAXM = 2500;
const int MAXK = 1000;
const LL INF = 1ll << 60;
int N, M, K;
int U[MAXM + 5], V[MAXM + 5], W[MAXM + 5];
LL G[MAXN + 5][MAXN + 5], One[MAXN + 5][MAXN + 5], C[MAXN + 5][MAXN + 5];
LL Mul(LL A[MAXN + 5][MAXN + 5], LL B[MAXN + 5][MAXN + 5]) {
for (int i = 1; i <= N; i++)
for (int j = 1; j <= N; j++) {
C[i][j] = INF;
for (int k = 1; k <= N; k++)
C[i][j] = std::min(C[i][j], A[i][k] + B[k][j]);
}
for (int i = 1; i <= N; i++)
for (int j = 1; j <= N; j++)
A[i][j] = C[i][j];
}
int main() {
scanf("%d%d%d", &N, &M, &K);
for (int i = 1; i <= N; i++)
for (int j = 1; j <= N; j++)
if (i != j)
G[i][j] = One[i][j] = INF;
for (int i = 1; i <= M; i++) {
int u, v, w; scanf("%d%d%d", &u, &v, &w);
G[u][v] = w, U[i] = u, V[i] = v, W[i] = w;
}
for (int k = 1; k <= N; k++)
for (int i = 1; i <= N; i++)
for (int j = 1; j <= N; j++)
G[i][j] = std::min(G[i][j], G[i][k] + G[k][j]);
if (!K)
return printf("%lld", G[1][N]), 0;
for (int k = 1; k <= M; k++)
for (int i = 1; i <= N; i++)
for (int j = 1; j <= N; j++)
One[i][j] = std::min(One[i][j], G[i][U[k]] + G[V[k]][j] - W[k]);
while (K) {
if (K & 1)
Mul(G, One);
Mul(One, One);
K >>= 1;
}
printf("%lld", G[1][N]);
return 0;
}