Atcoder Beginner Contest 236 G Good Vertices

G. Good Vertices

题目大意

给定一张有向图,初始无边,然后每一时刻都会加一条边,可能会有自环,但无重边。问每个点最早在第几时刻,存在一条从\(1\)号点到该点的长度为\(l\)的路径。

解题思路

问题首先可以转化成,求\(1\)号点到\(i\)号点的所有 长度为\(l\)路径中添加边时刻的最大值 的最小值。

\(dp[i][j]\)表示从 \(1\)号点出发长度为 \(l\)到达 \(i\)号点的答案,则 \(dp[i][j] = \min_k(\max(dp[k][j - 1], w(k, i)))\),其中 \(w(k,i)\)表示边 \((k,i)\)添加的时刻。

时间复杂度为 \(O(n^2 L)\),而 \(L\)高达 \(10^9\)

关于路径长度的问题一般的优化就是矩阵快速幂来求,但一般都是需要递推式是线性的。

官方题解的描述来看,这里涉及到两个运算 \(\min\)\(\max\),在抽象代数中 \((Z \cup {\infty}, min, max)\)是构成半环(环的基础上把逆元条件去掉)的,即 \((Z \cup {\infty}, min)\)是交换幺半群, \((Z, max)\)是半群,(当然反过来也是),由此俩运算组成的矩阵乘法具有结合律,因而可以用矩阵快速幂优化。

虽说这学期刚学了近世代数,对于半环的矩阵运算具有结合律留待证明

神奇的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

const int inf = 1e9 + 7;

int main(void) {
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);
    int n, t, l;
    cin >> n >> t >> l;
    vector<vector<int>> ma(n, vector<int>(n, inf));
    auto result = ma;
    for(int i = 1; i <= t; ++ i){
        int u, v;
        cin >> u >> v;
        -- u;
        -- v;
        ma[u][v] = i;
    }
    auto multi = [](const vector<vector<int>> &a, const vector<vector<int>> &b){
        vector<vector<int>> c(a.size(), vector<int>(b[0].size(), inf));
        for(int i = 0; i < a.size(); ++ i)
            for(int j = 0; j < b[0].size(); ++ j)
                for(int k = 0; k < a[0].size(); ++ k){
                    c[i][j] = min(c[i][j], max(a[i][k], b[k][j]));
                }
        return c;
    };
    for(int i = 0; i < n; ++ i)
        result[i][i] = -inf;
    while(l){
        if (l & 1)
            result = multi(result, ma);
        ma = multi(ma, ma);
        l >>= 1;
    }
    for(int i = 0; i < n; ++ i){
        if (result[0][i] == inf)
            result[0][i] = -1;
        cout << result[0][i] << " \n"[i == n - 1];
    }
    return 0;
}


posted @ 2022-01-27 16:31  ~Lanly~  阅读(127)  评论(0编辑  收藏  举报