题目
分析
表示 到 用 次魔法的最小代价, 表示 到 用一次魔法的最小代价:
枚举的中转节点 恰似 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;
}