[Luogu P3597] [BZOJ 4386] [POI2015]WYC

版权声明:欢迎转载蒟蒻博客,但请注明出处: https://blog.csdn.net/LPA20020220/article/details/84348331

洛谷传送门

BZOJ传送门

题目描述

给定一张 n n 个点 m m 条边的带权有向图,每条边的边权只可能是 1 1 2 2 3 3 中的一种。将所有可能的路径按路径长度排序,请输出第 k k 小的路径的长度,注意路径不一定是简单路径,即可以重复走同一个点。

输入输出格式

输入格式:

第一行包含三个整数 n , m , k ( 1 n 40 1 m 1000 1 k 1 0 18 ) n,m,k(1\le n\le 40,1\le m\le 1000,1\le k\le 10^{18}) 。接下来 m m 行,每行三个整数 u , v , c ( 1 u , v n u v 1 c 3 ) u,v,c(1\le u,v\le n,u\ne v,1\le c\le 3) ,表示从 u u 出发有一条到 v v 的单向边,边长为 c c 。可能有重边。

输出格式:

包含一行一个正整数,即第 k k 短的路径的长度,如果不存在,输出 1 -1

输入输出样例

输入样例#1:

6 6 11
1 2 1
2 3 2
3 4 2
4 5 1
5 3 1
4 6 3

输出样例#1:

4

说明

给定一张 n n 个点 m m 条边的带权有向图,每条边的边权只可能是 1 1 2 2 3 3 中的一种。

将所有可能的路径按路径长度排序,请输出第 k k 小的路径的长度,注意路径不一定是简单路径,即可以重复走同一个点。

解题分析

明显是矩阵快速幂统计路径个数。 这里类似用倍增的方法, 预处理出转移 2 k 2^k 次后的矩阵, 再从大到小尝试加回来。

统计长度 m \le m 的路径个数, 只需要添加每个点到 0 0 节点, 以及 0 0 0\to 0 的边即可。 但因为第一次未转移的时候多算了一次, 所以注意减一。

代码如下:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ll long long
#define MX 125
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    for (; !isdigit(c); c = gc);
    for (;  isdigit(c); c = gc)
    x = (x << 1) + (x << 3) + c - 48;
}
int n, m, bd;
ll k, ans;
struct Matrix {ll mat[MX][MX];} mp[66], buf, now;
IN Matrix operator * (const Matrix &x, const Matrix &y)
{
    Matrix ret;
    R int i, j, k;
    for (i = 0; i <= bd; ++i)
    for (j = 0; j <= bd; ++j)
    {
        ret.mat[i][j] = 0;
        for (k = 0; k <= bd; ++k)
        ret.mat[i][j] += x.mat[i][k] * y.mat[k][j];
    }
    return ret;
}
IN bool count(const Matrix &tar)
{
    ll ret = 0;
    for (R int i = 1; i <= n; ++i)
    if ((ret += tar.mat[i][0] - 1) >= k) return true;
    return false;
}
IN ll calc(const Matrix &tar)
{
    ll ret = 0;
    for (R int i = 1; i <= n; ++i)
    ret += tar.mat[i][0] - 1;
    return ret;
}
int main(void)
{
    int a, b, c, cur;
    in(n), in(m), in(k); bd = n * 3;
    mp[0].mat[0][0] = 1;
    for (R int i = 1; i <= n; ++i)
    {
        mp[0].mat[i][0] = 1;
        mp[0].mat[i][i + n] = 1;
        mp[0].mat[i + n][i + 2 * n] = 1;
    }
    for (R int i = 1; i <= m; ++i)
    {
        in(a), in(b), in(c);
        mp[0].mat[a + (c - 1) * n][b]++;
    }
    for (cur = 1; ; ++cur)
    {
        if (cur > 64) return puts("-1"), 0;
        mp[cur] = mp[cur - 1] * mp[cur - 1];
        if (count(mp[cur])) break;
    }
    for (R int i = 0; i <= bd; ++i) now.mat[i][i] = 1;
    for (--cur; ~cur; --cur)
    {
        buf = now * mp[cur];
        if (!count(buf)) now = buf, ans += 1ll << cur;
    }
    printf("%lld", ans);
}

猜你喜欢

转载自blog.csdn.net/LPA20020220/article/details/84348331