Luogu P2656采蘑菇(Tarjan + spfa)

采蘑菇

题目描述

小胖和 ZYR 要去 ESQMS 森林采蘑菇。

ESQMS 森林间有 \(N\) 个小树丛,\(M\) 条小径,每条小径都是单向的,连接两个小树丛,上面都有一定数量的蘑菇。小胖和 ZYR 经过某条小径一次,可以采走这条路上所有的蘑菇。由于 ESQMS 森林是一片神奇的沃土,所以一条路上的蘑菇被采过后,又会长出一些新的蘑菇,数量为原来蘑菇的数量乘上这条路的“恢复系数”,再下取整。

比如,一条路上有 \(4\) 个蘑菇,这条路的“恢复系数”为 \(0.7\),则第一~四次经过这条路径所能采到的蘑菇数量分别为 \(4,2,1,0\)

现在,小胖和 ZYR 从 \(S\) 号小树丛出发,求他们最多能采到多少蘑菇。

思路

由于每一条边都是可以重复经过得,所以考虑找到一个环使得将环内所有得蘑菇都采集完后,再进入下一个环,这样得采集方式可以使得最后得答案最大。那么这些环上得点可以缩成一个点,让这个环上所有得贡献都变成缩点后得点权。将所有得联通分量缩点之后,就是一个有向无环得图,这样就可以考虑从起点 \(S\) 出发求得到达每一个点得最大贡献是多少从而确定最后得答案,也就是在 \(DAG\) 上用 \(spfa\) 跑一遍最长路。

    std::cin.tie(nullptr)->sync_with_stdio(false);

    int n, m;
    std::cin >> n >> m;
    std::vector<std::array<int, 3>> G[n + 1];
    for (int i = 1; i <= m; i++) {
        int u, v, w;
        double k;
        std::cin >> u >> v >> w >> k;
        G[u].push_back({v, w, k * 10});
    }

    std::vector<int> dfn(n + 1), stk(n + 1), low(n + 1);
    std::vector<int> scc(n + 1);
    std::vector<bool> vis(n + 1);
    int idx = 0, top = 0, cnt = 0;
    // idx是时间戳,top是栈的指针,cnt是强连通分量的数量->缩点的个数

    std::function<void(int)> tarjan = [&] (int u) -> void {
        dfn[u] = low[u] = ++ idx;
        stk[++top] = u;
        vis[u] = true;

        for (auto& [v, w, k] : G[u]) {
            if (!dfn[v]) {
                tarjan(v);
                low[u] = std::min(low[u], low[v]);
            } else if (vis[v]) {
                low[u] = std::min(low[u], dfn[v]);
            }
        }

        if (dfn[u] == low[u]) {//时间戳和回溯点是同一个那就是缩点
            int v = 0;
            cnt++;
            do {
                v = stk[top--];
                vis[v] = false;
                scc[v] = cnt;
            } while(v != u);
        }
    };

    for (int i = 1; i <= n; i++) {
        if (!dfn[i]) tarjan(i);
    }

    std::vector<std::array<int, 2>> adj[cnt + 1];
    std::vector<int> sum(cnt + 1);
    for (int i = 1; i <= n; i++) {
        for (auto& [v, w, k] : G[i]) {
            if (scc[i] == scc[v]) {
                while(w) sum[scc[i]] += w, w = w * k / 10;
            } else {
                adj[scc[i]].push_back({scc[v], w});
            }
        }
    }

    int s; std::cin >> s;
    s = scc[s];

    std::queue<int> q;
    q.push(s);
    std::vector<int> dist(cnt + 1, -1);
    std::vector<bool> st(cnt + 1);
    st[s] = true;
    dist[s] = sum[s];
    while(!q.empty()) {
        int u = q.front();
        q.pop();
        st[u] = false;

        for (auto& [v, w] : adj[u]) {
            if (dist[v] < dist[u] + w + sum[v]) {
                dist[v] = dist[u] + w + sum[v];
                if (!st[v]) {
                    st[v] = true;
                    q.push(v);
                }
            }
        }
    }

    std::cout << *max_element(all(dist)) << "\n";

    return 0 ^ 0;
posted @ 2022-10-21 15:29  浅渊  阅读(42)  评论(0)    收藏  举报