【Luogu P2886】[USACO07NOV]奶牛接力
题目链接:
题目大意:
给出一张无向连通图,求 \(S\) 到 \(E\) 经过 \(k\) 条边的最短路。
正文:
题目中 \(u,v\leq 1000\),但是题目里的图是无向连通图,而 \(T\leq100\),也就是说,我们可以先离散化一下方便计算和优化空间复杂度。
而问题中要求的是最短路,我们可以定义广义矩阵乘法 \(A\times B=C\) 为
\[C_{i,j}=\min_{k=1}^{n}(A_{i,k}+B_{k,j})
\]
你会发现这其实是个 Floyd。接着用这个广义矩阵乘法来快速幂 \(k-1\) 次题目中的邻接矩阵就能得到答案了。
代码:
struct matrix
{
ll mat[N][N];
int n, m;
matrix(){memset(mat, 127 / 3, sizeof mat);}
inline ll* operator [] (int b) { return mat[b];}
}f;
inline matrix operator*(matrix &a, matrix &b)
{
matrix c; c.n = a.n, c.m = b.m;
for (int k = 1; k <= a.m; k++)
for (int i = 1; i <= a.n; i++)
for (int j = 1; j <= b.m; j++)
c[i][j] = min(c[i][j] , a[i][k] + b[k][j]);
return c;
}
matrix qpow(matrix a, ll b)
{
b--;
matrix ans;
ans = a;
for (; b; b >>= 1)
{
if(b & 1) ans = ans * a;
a = a * a;
}
return ans;
}
int bucket[N * 10], cnt;
int main()
{
scanf ("%d%d%d%d", &k, &m, &s, &t);
for (int i = 1; i <= m; i++)
{
int u, v, w;
scanf ("%d%d%d", &w, &u, &v);
if(!bucket[u]) bucket[u] = ++cnt;
if(!bucket[v]) bucket[v] = ++cnt;
f[bucket[u]][bucket[v]] = f[bucket[v]][bucket[u]] = w;
}
f.n = f.m = cnt;
f = qpow(f, k);
printf ("%d\n", f[bucket[s]][bucket[t]]);
return 0;
}