「解题报告」UOJ32 [UR #2] 跳蚤公路

图论好难啊。

首先明确题目要求的其实就是从 \(1\)\(u\) 是否能够经过一个负环。首先容易得到如果存在负环,那么一定存在一个简单负环,所以只需要考虑简单环。

考虑如何判断负环:Floyd 和 Bellman-Ford。

为什么不用 SPFA

______,___。

Bellman-Ford 这么好写为什么不写。

Floyd 复杂度太高了,考虑 Bellman-Ford。正常 Bellman-Ford 是设 \(f_{i, u}\) 表示从 \(1\)\(u\) 走最多 \(i\) 步的最短路,那么假如存在某个 \(f_{n, u} < f_{n - 1, u}\),那么说明存在负环。如果 \(x\) 固定,我们就容易找出负环,那么能过经过负环的点就是这个 \(u\) 能到达的所有点。

容易发现,对于任意一条路径,我们可以将其长度写为 \(kx+b\) 的形式。而由于我们只考虑简单环,那么 \(k \in [-n, n]\),对于某个 \(k\) 来说,我们肯定只考虑 \(b\) 最小的路径。那么我们可以将上面的东西拓展一维,设 \(f_{i, u, k}\) 表示从 \(1\)\(u\) 走最多 \(i\) 步,路径长 \(kx+b\)\(b\) 的最小值,那么 \(i\)\(u\) 的最小值就是 \(\min_k f_{i, u, k}\)。那么存在负环的条件就是 \(\min_k kx + f_{n, u, k} < \min_j jx + f_{n - 1, u, j}\)。对于某个点可以经过负环的 \(x\) 的取值范围,就是所有能够到达这个点的负环的取值范围的并集。

\(\min\) 拆开,相当于对每个 \(k\)\(kx + f_{n, u, k} < \min_j jx + f_{n - 1, u, j}\)。求 \(kx + f_{n, u, k} < \min_j jx + f_{n - 1, u, j}\) 的取值范围,相当于对每个 \(j\)\(kx + f_{n, u, k} < jx + f_{n - 1, u, j}\) 的取值范围的交集,直接求即可。

最后得到每个点的并集之后,取个反即可。细节很多,有点难实现,具体实现看代码。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 105, MAXM = 10005;
const long long INF = 0x3f3f3f3f3f3f3f3f;
int n, m;
int u[MAXM], v[MAXM], w[MAXM], s[MAXM];
long long f[MAXN][MAXN][MAXN << 1];
bool vis[MAXN];
vector<int> e[MAXN];
void dfs(int u) {
    vis[u] = 1;
    for (int v : e[u]) if (!vis[v]) {
        dfs(v);
    }
}
long long floordiv(long long a, long long b);
long long ceildiv(long long a, long long b);
long long floordiv(long long a, long long b) {
    if (!a) return 0;
    if (b < 0) a *= -1, b *= -1;
    if (a > 0) return a / b;
    else return -ceildiv(-a, b);
}
long long ceildiv(long long a, long long b) {
    if (!a) return 0;
    if (b < 0) a *= -1, b *= -1;
    if (a > 0) return (a + b - 1) / b;
    else return -floordiv(-a, b);
}
vector<pair<long long, long long>> range[MAXN];
void chkmin(long long &a, long long b) {
    a = min(a, b);
}
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++) {
        scanf("%d%d%d%d", &u[i], &v[i], &w[i], &s[i]);
        e[u[i]].push_back(v[i]);
    }
    memset(f, 0x3f, sizeof f);
    for (int i = 0; i < n; i++) {
        chkmin(f[i][1][MAXN], 0);
        for (int k = -n; k <= n; k++) {
            for (int j = 1; j <= m; j++) if (f[i][u[j]][k + MAXN] < INF) {
                chkmin(f[i + 1][v[j]][k + MAXN + s[j]], f[i][u[j]][k + MAXN] + w[j]);
            }
        }
    }
    for (int v = 1; v <= n; v++) {
        for (int i = 1; i <= n; i++) {
            vis[i] = 0;
        }
        dfs(v);
        for (int k = -n; k <= n; k++) if (f[n][v][k + MAXN] < INF) {
            long long l = -INF, r = INF;
            for (int j = -n; j <= n; j++) if (f[n - 1][v][j + MAXN] < INF) {
                long long p = f[n - 1][v][j + MAXN] - f[n][v][k + MAXN];
                if (k - j > 0) {
                    r = min(r, ceildiv(p, k - j) - 1);
                } else if (k - j == 0) {
                    if (p <= 0) {
                        l = INF, r = -INF;
                        break;
                    }
                } else {
                    l = max(l, floordiv(p, k - j) + 1);
                }
            }
            if (l <= r) {
                for (int i = 1; i <= n; i++) if (vis[i])
                    range[i].push_back({l, r});
            }
        }
    }
    for (int i = 1; i <= n; i++) {
        if (range[i].empty()) {
            printf("-1\n");
            continue;
        }
        long long ans = 0;
        sort(range[i].begin(), range[i].end(), [](auto a, auto b) { return a.second < b.second; });
        for (int j = range[i].size() - 1; j; j--) {
            range[i][j - 1].first = min(range[i][j - 1].first, range[i][j].first);
        }
        ans += range[i].front().first - (-INF);
        ans += INF - range[i].back().second;
        for (int j = 1; j < range[i].size(); j++) {
            ans += max(range[i][j].first - range[i][j - 1].second - 1, 0ll);
        }
        if (ans > 1000000000000000000ll) ans = -1;
        printf("%lld\n", ans);
    }
    return 0;
}
posted @ 2023-04-11 21:09  APJifengc  阅读(72)  评论(2编辑  收藏  举报