忘掉过去的人,终将重蹈覆辙。|

chenwenmo

园龄:6个月粉丝:0关注:7

P8906 [USACO22DEC] Breakdown P [最短路]

P8906 [USACO22DEC] Breakdown P

Solution

  • 经典 trick,删边比较难处理,转换成加边,倒着处理。

  • 那我们接下来要考虑,怎么记录状态,以及,每加一次边要如何更新状态。

  • 还是比较套路地,我们可以求出 1 到某个点 i 经过 k/2 条边的最短路,再求出 in 经过 kk/2 条边的最短路。根据这种思想,我们可能会思考折半搜索,分治之类的算法来解决这道题。

  • 如果我们要求 k/2,就要求 k/4,然后又要求 k/8。注意数据范围 k8,这种数据范围可能会成为解题的关键。那么,k/24k/42k/811 显然就是边界,就等于两点间的边权。如何求 2 呢?是不是只需要合并两条边。求 4 只需要把 2 的答案合并。这样一来,我们要记录的信息就非常少,不需要去写分治之类的递归程序。

  • 至于怎么合并。我们先考虑 1 到某一点 i 的情况,in 同理。我们设 fi 表示 1i 经过 k/2 条边的最短路,设 gi,j 表示 ij 只经过两条边的最短路。为啥 f 只记录终点,g 要记录起点和终点,因为 f 显然只有终点才是有用的,g 的话要记录起点和终点才能合并给 f,不然咋合并?

  • 先大致口胡一遍算法。

    • 如果 k/2=4,那 g+gf
    • 如果 k/2=3,那么 g+wfw 是边权。
    • 如果 k/2=2,那么 gf
    • 如果 k/2=1,那么 wf
  • 那我们接下来考虑如何更新状态,且是在正确的复杂度内。

  • 先考虑如何更新 g。假设当前加的是边 (uv),那么 iuv 这条路径可能被更新 (i 是任意点),O(n) 枚举一个 i,路径 uvj 也可能被更新,再 O(n) 枚举一个 j。更新 g 的总复杂度 O(n)

  • 考虑如何更新 f,这里以 k/2=4 为例。

    • 假设 fi 经过的是这样一条边 1uvji

    • 如果当前加的边是 ji,那么只需要 O(n) 枚举一个 v,然后 g1,v+gv,ifi 即可。

    • 如果当前加的边是 vj,只需要 O(n) 枚举 i,然后 g1,v+gv,ifi

    • 如果当前加的边是 uvO(n) 枚举 i,然后也是 g1,v+gv,ifi。(欸,怎么写起来都是一样的,不管了qwq)

    • 以上情况的总复杂度都是 O(n)

    • 那如果当前加入的是 1u 呢?我们肯定要 O(n2) 枚举 vi,但是注意到,起点为 1 的边只有 O(n) 条,于是均摊复杂度只有 O(n3),还是能过的。

  • 综上,总复杂度 O(n3),于是就可以愉快地 coding 了。

Code

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;

const int N = 3e2 + 5, M = N * N, inf = 0x3f3f3f3f;

int n, m, k, w[N][N], ans[M];

void Min(int &a, int b) {return a = min(a, b), void();}

struct Edge {int u, v;} e[M];

struct Worker {
    int f[N]; // st(起点) 到 i 的经过 k 条边的最短路
    int g[N][N]; // i 到 j 经过 2 条边的最短路
    int w[N][N]; // 边权
    int st, k;
    void init(int _st, int _k) {
        st = _st, k = _k;
        memset(f, 0x3f, sizeof(f));
        memset(g, 0x3f, sizeof(g));
        memset(w, 0x3f, sizeof(w));
    }
    void add(int u, int v, int _w) {
        w[u][v] = _w;
        if (k == 1) {
            if (u == st) f[v] = w[u][v];
            return;
        }
        for (int i = 1; i <= n; i++) {
            Min(g[i][v], w[i][u] + w[u][v]);
            Min(g[u][i], w[u][v] + w[v][i]);
        }
        if (k == 2) {
            for (int i = 1; i <= n; i++) f[i] = g[st][i];
            return;
        }
        if (k == 3) {
            if (u == st) {
                for (int i = 1; i <= n; i++) {
                    for (int j = 1; j <= n; j++) {
                        Min(f[i], g[u][j] + w[j][i]);
                    }
                }
            } else {
                for (int i = 1; i <= n; i++) {
                    Min(f[i], w[st][u] + g[u][i]);
                    Min(f[i], w[st][v] + g[v][i]);
                    // Min(f[u], w[st][i] + g[i][u]); 该情况不会被更新
                    Min(f[v], w[st][i] + g[i][v]);
                }
            }
        }
        if (k == 4) {
            if (u == st) {
                for (int i = 1; i <= n; i++) {
                    for (int j = 1; j <= n; j++) {
                        Min(f[i], g[u][j] + g[j][i]);
                    }
                }
            } else {
                for (int i = 1; i <= n; i++) {
                    Min(f[i], g[st][u] + g[u][i]);
                    Min(f[i], g[st][v] + g[v][i]);
                    // Min(f[u], g[st][i] + g[i][u]); 该情况不会被更新
                    Min(f[v], g[st][i] + g[i][v]);
                }
            }
        }
    }
}work1, workn;

int main() {
    cin >> n >> k;
    m = n * n;
    for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) cin >> w[i][j];
    for (int i = 1; i <= m; i++) cin >> e[i].u >> e[i].v;
    int k1 = k / 2, kn = k - k1;
    work1.init(1, k1), workn.init(n, kn);
    for (int i = m; i >= 1; i--) {
        ans[i] = inf;
        for (int j = 1; j <= n; j++) ans[i] = min(ans[i], work1.f[j] + workn.f[j]);
        work1.add(e[i].u, e[i].v, w[e[i].u][e[i].v]);
        workn.add(e[i].v, e[i].u, w[e[i].u][e[i].v]);
    }
    for (int i = 1; i <= m; i++) cout << (ans[i] == inf ? -1 : ans[i]) << "\n";
    return 0;
}```

本文作者:chenwenmo

本文链接:https://www.cnblogs.com/chenwenmo/p/18535931

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   chenwenmo  阅读(6)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起