[USACO07NOV]牛继电器Cow Relays
矩阵快速幂+倍增floyd
这道题十分神啊,floyd与矩阵快速幂(思想)结合。
矩阵快速幂的原理与普通快速幂一样,因为矩阵乘法满足交换律。
而这道题是让我们求从s出发恰好经过k条边(k<=1000000)到达t的最短路。如何考虑?图有一个性质,我们令矩阵Ci j表示i j之间是否存在边,那么矩阵的k次幂就是任意点走过恰好k步能到达的点(值就是方案数)。利用这个思想,我们考虑floyd的实现,传统的floyd是在一个矩阵中转移,可以走任意步。我们改一改:
//floyd
gk fina;
for (int i=1;i<=tot;++i)
for (int j=1;j<=tot;++j)
fina.m[i][j]=INF;
for (int k=1;k<=tot;++k)
for (int i=1;i<=tot;++i)
for (int j=1;j<=tot;++j)
{
fina.m[i][j]=min(fina.m[i][j],a.m[i][k]+b.m[k][j]);
}
return fina;
//快速幂
while (k)
{
if (k&1) e=floyd(e,firs);
firs=floyd(firs,firs);
k>>=1;
}
firs就是一开始的图,e初始值除了对角线以外其余都是INF。所以第一遍只能求出恰好一遍能走到的点,第二次就能求出恰好两步的,下一次恰好4步。。。最终倍增地求出答案.
具体实现:用走 i 步的最短路和走 j 步的最短路生成一个答案:答案就是走 i*j 步的最短路
code:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<cstring>
#include<vector>
#include<stack>
#define int long long
#define SUPER_INT signed
const int maxn=106;
using namespace std;
const int INF=1e17;
struct gk
{
int m[maxn][maxn];
};
int s,t,k,m,id[6000000],tot;
gk firs,ans;
inline gk floyad(gk a,gk b)
{
gk fina;
for (int i=1;i<=tot;++i)
for (int j=1;j<=tot;++j)
fina.m[i][j]=INF;
for (int k=1;k<=tot;++k)
for (int i=1;i<=tot;++i)
for (int j=1;j<=tot;++j)
{
fina.m[i][j]=min(fina.m[i][j],a.m[i][k]+b.m[k][j]);
}
return fina;
}
gk e;
inline gk fast_pow(int k)
{
for (int i=1;i<=tot;++i) e.m[i][i]=0;
while (k)
{
if (k&1) e=floyad(e,firs);
firs=floyad(firs,firs);
k>>=1;
}
return e;
}
SUPER_INT main()
{
for (int i=1;i<=105;++i)
for (int j=1;j<=105;++j)
firs.m[i][j]=ans.m[i][j]=e.m[i][j]=INF;
cin>>k>>m>>s>>t;
for (int i=1,x,y,c;i<=m;++i)
{
scanf("%lld%lld%lld",&c,&x,&y);
if (!id[x]) id[x]=++tot;
if (!id[y]) id[y]=++tot;
firs.m[id[x]][id[y]]=firs.m[id[y]][id[x]]=c;
}
gk ans=fast_pow(k);
cout<<ans.m[id[s]][id[t]];
return 0;
}
收获:
矩阵类问题注意打表考虑(手算太慢了,还要记住结论:矩阵的k次幂的性质,矩阵快速幂的实现,以及floyd与矩阵具有很好的相容性。