洛谷1613 跑路 倍增 + Floyd

首先,我们一定要认识到本题中的最短时间所对应的道路不一定是在起点到终点的最短路。例如,起点到终点的最短路为 1515 ,那么对 1515 进行二进制拆分的话是 11111111 ,这时求出的最短时间为4。然而如果有一条长度为 1616 的路径的话最短时间就为 11,显然比之前求的更优 。
我们在这里定义两个数组:

  1. intint d[i][j]d[i][j],即代表点 (i,j)(i,j) 之间的最短跑步时间。
  2. boolbool g[i][j][k]g[i][j][k],它代表的是点 (i,j)(i,j) 之间是否有一条跑步时间为 2k2^k 的一条道路。
    我们对 g[i][j][k]g[i][j][k] 运行一遍 FloydFloyd 来更新。
    更新方程是:
    if(g[i][k][log1]if(g[i][k][log-1] && g[k][j][log1])g[k][j][log-1]) g[i][j][log]=1g[i][j][log]=1d[i][j]=1d[i][j]=1.

显然,我们处理完 gg 数组后所有可以在一秒之内到达的点对都已处理完毕。于是我们在图上对 d[i][j]d[i][j] 再跑一遍 FloydFloyd 即可。
时间复杂度为 O(n3logm)O(n^3logm)

#include<cstdio>
#include<algorithm>
using namespace std;
const int logn = 60;
const int maxm = 500000 + 5;
const int maxn = 60;
const int inf  = 100000000;
int d[maxn][maxn];
bool g[maxn][maxn][logn];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= n;++i)
       for(int j = 1;j <= n;++j)d[i][j] = inf;
    for(int i = 1;i <= m;++i)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        d[a][b] = 1, g[a][b][0] = 1;
    }
    for(int p = 1; p < logn; ++p)
        for(int k = 1; k <= n;++k)
            for(int i = 1; i <= n; ++i)
                for(int j = 1; j <= n;++j)
                {
                    if(g[i][k][p-1] && g[k][j][p-1]) g[i][j][p] = 1, d[i][j] = 1;
                }
    for(int k = 1;k <= n;++k)
        for(int i = 1;i <= n;++i)
            for(int j = 1;j <= n;++j)
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
    printf("%d",d[1][n]);
    return 0;
}
原文地址:https://www.cnblogs.com/guangheli/p/9845116.html