1. 程式人生 > >[Luogu P3597] [BZOJ 4386] [POI2015]WYC

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

洛谷傳送門

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);
}