题目:BZOJ1706
解析:
矩阵快速幂。
首先将起点终点离散化降至
100
100
100以内。
考虑最裸的状态转移,令
f
[
k
]
[
i
]
[
j
]
f[k][i][j]
f[k][i][j]表示经过
k
k
k条边从
i
i
i到
j
j
j的最短路长度,就有:
f
[
k
]
[
i
]
[
j
]
=
m
i
n
{
f
[
k
−
1
]
[
i
]
[
m
i
d
]
+
a
[
m
i
d
]
[
j
]
}
f[k][i][j]=min\{f[k-1][i][mid]+a[mid][j ]\}
f[k][i][j]=min{f[k−1][i][mid]+a[mid][j]}
这样直接转移复杂度是
O
(
N
∗
10
0
3
)
O(N*100^3)
O(N∗1003)的考虑去掉
k
k
k部分后实际上就是一个矩阵乘法,那么相当于是求初始矩阵的
k
k
k次方,矩阵快速幂即可。
总结:若发现递推式很像矩阵乘法,并且满足结合律,那么就可以大胆地用矩阵乘法/矩阵快速幂求解。
代码:
#include <bits/stdc++.h>
using namespace std;
const int Max=105;
int n,m,s,t,k,rev[Max*10];
struct matrix{int a[Max][Max];}ans,v;
inline matrix mul(const matrix&a,const matrix&b)
{
matrix c;
memset(c.a,0x3f,sizeof(c.a));
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
c.a[i][j]=min(c.a[i][j],a.a[i][k]+b.a[k][j]);
return c;
}
inline void solve()
{
while(k)
{
if(k&1) ans=mul(ans,v);
k>>=1,v=mul(v,v);
}
}
int main()
{
memset(v.a,0x3f,sizeof(v.a));
memset(ans.a,0x3f,sizeof(ans.a));
scanf("%d%d%d%d",&k,&m,&s,&t);
for(int i=1,a,b,c;i<=m;i++)
{
scanf("%d%d%d",&c,&a,&b);
if(!rev[a]) rev[a]=++n;
if(!rev[b]) rev[b]=++n;
v.a[rev[a]][rev[b]]=v.a[rev[b]][rev[a]]=min(v.a[rev[b]][rev[a]],c);
}
for(int i=1;i<=n;i++) ans.a[i][i]=0;
solve();
printf("%d",ans.a[rev[s]][rev[t]]);
return 0;
}