bzoj 1576 [Usaco2009 Jan]安全路经Travel dijkstra+并查集/树链剖分

题面

题目传送门

解法

最短路树……emmmm,从未听说过……

  • 因为题目已经保证从 1 到其他所有点的最短路是唯一的,所以我们不妨把这些边全都记下来,然后就可以发现这些边共同构成一棵树
  • 首先考虑为什么这是一棵树。显然,我们只要说明这些边不会构成环即可。如果出现环,那么说明从1到环上的点一定由至少2条最短路,和题目描述出现矛盾,所以这一定是一棵树
  • 然后我们考虑,一条不在树上的边 ( x , y , v ) 会对哪些点的答案造成影响。显然,一定是树上 x y 路径上的所有点(不包括这两个点的 l c a )。假设路径上有一点 i ,那么对 i 的影响就是 d i s [ x ] + d i s [ y ] d i s [ i ] + v 。因为 d i s [ i ] 对于 i 是一个固定的值,所以我们只要求出每一个点对它造成影响的边 ( x , y , v ) d i s [ x ] + d i s [ y ] + v 最小的那一个就可以了
  • 这个显然可以用树剖+线段树维护吧,时间复杂度 O ( ( m + n ) log n + m log 2 n ) ,可以通过此题
  • 我太懒了,不想写树剖
  • 但是我们可以发现,如果对于每一条不在树上的边 ( x , y , v ) 按照 d i s [ x ] + d i s [ y ] + v 从小到大排序,那么每一个点的答案一定会被第一次能影响到它的边更新,不必每一次都更新答案
  • 那么,我们就可以使用并查集来维护这一个过程,每一次在更新的时候可以直接跳过已经求出答案的部分
  • 时间复杂度: O ( ( m + n ) log n + m α ( n ) )

代码

#include <bits/stdc++.h>
#define int long long
#define PI pair <int, int>
#define mp make_pair
#define N 100010
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
    x = 0; int f = 1; char c = getchar();
    while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
    while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Edge {
    int next, num, v;
} e[N * 5];
struct Node {
    int x, y, val;
    bool operator < (const Node &a) const {
        return val < a.val;
    }
} a[N * 5];
int n, m, cnt, p[N], fa[N], ans[N], dis[N], used[N];
void add(int x, int y, int v) {
    e[++cnt] = (Edge) {e[x].next, y, v};
    e[x].next = cnt;
}
void dijkstra(int s) {
    for (int i = 1; i <= n; i++)
        dis[i] = INT_MAX, used[i] = 0;
    priority_queue <PI, vector <PI>, greater <PI> > h;
    dis[s] = 0; h.push(mp(dis[s], s));
    while (!h.empty()) {
        PI tmp = h.top(); h.pop();
        int x = tmp.second;
        if (used[x]) continue; used[x] = 1;
        for (int p = e[x].next; p; p = e[p].next) {
            int k = e[p].num, v = e[p].v;
            if (dis[k] > dis[x] + v) {
                dis[k] = dis[x] + v, fa[k] = x;
                h.push(mp(dis[k], k));
            }
        }
    }
}
int Find(int x) {
    if (p[x] == x) return x;
    return p[x] = Find(p[x]);
}
main() {
    read(n), read(m); cnt = n;
    for (int i = 1; i <= m; i++) {
        int x, y, v; read(x), read(y), read(v);
        add(x, y, v);
    }
    dijkstra(1); int tot = 0;
    for (int i = 1; i <= n; i++)
        for (int p = e[i].next; p; p = e[p].next) {
            int k = e[p].num, v = e[p].v;
            a[++tot] = (Node) {i, k, dis[i] + dis[k] + v};
        }
    sort(a + 1, a + tot + 1);
    for (int i = 1; i <= n; i++) p[i] = i;
    memset(ans, -1, sizeof(ans));
    for (int i = 1; i <= tot; i++) {
        int x = a[i].x, y = a[i].y, v = a[i].val;
        if (x == fa[y] || y == fa[x]) continue;
        x = Find(x), y = Find(y);
        while (x != y) {
            if (dis[x] < dis[y]) swap(x, y);
            ans[x] = v - dis[x];
            p[x] = Find(fa[x]), x = Find(x);
        }
    }
    for (int i = 2; i <= n; i++) cout << ans[i] << "\n";
    return 0;
}

猜你喜欢

转载自blog.csdn.net/emmmmmmmmm/article/details/82111427