「LCA + 最小生成树」货车运输

货车运输

原题链接 货车运输

题目大意

(n) 个点,然后给你 (m) 条边,每条边我们有边权 (k) ,现在给你 (q) 次询问,每次询问两个整数 (x, y),表示从 (x)(y) 的最大值为多少,这个值就是走过的边权的最小值。

题目题解

这道题很明显可以得到的是,一些边我们从始至终都不会走的,因为有别的更优的路径,这让我们想到了最大生成树了,那求出最大生成树之后怎么办呢?我们发现,对于两个点而言,在最大生成树中有且仅有一条路径可以互通,如何让电脑直接走这条路呢,嗯?是不是很熟悉,对,就是LCA,求最近公共祖先,但光只有LCA肯定是不行的,因为我们这次带上了边权,其实问题很简单,根据题目大意我们得到:我们的问题是找到我们走的边的最小边权,那么就意味着我们可以维护一个与记录(x)点的(2^k)的数组一样的数组,维护(x)点走(2^k)后所能得到的最小值,那么这样我们就能得到我们的答案了

代码如下

//#define fre yes

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define INF 9999999

const int N = 50005;
struct Node {
    int x, y, z;
}g[N];

int head[N], to[N], ver[N], edge[N];
int par[N];
int depth[N], f[N][22], w[N][22], lg[N];
bool Vis[N];

int n;

void init() {
    for (int i = 1; i <= n; i++) {
        par[i] = i;
    }
}

int tot;
void addedge(int x, int y, int z) {
    ver[tot] = y;
    edge[tot] = z;
    to[tot] = head[x];
    head[x] = tot++;
}

bool cmp(Node x, Node y) {
    return x.z > y.z;
}

int find(int x) {
    if(x == par[x]) return par[x];
    else return par[x] = find(par[x]);
}

void dfs(int x, int fa) {
    Vis[x] = 1;
    depth[x] = depth[fa] + 1;
    f[x][0] = fa;
    for (int i = 1; (1 << i) <= depth[x]; i++) {
        f[x][i] = f[f[x][i - 1]][i - 1];
        w[x][i] = std::min(w[x][i - 1], w[f[x][i - 1]][i - 1]);
    }

    for (int i = head[x]; ~i; i = to[i]) {
        int v = ver[i];
        if(v != fa) {
            w[v][0] = edge[i];
            dfs(v, x);
        }
    }
}

int lca(int x, int y) {
    if(find(x) != find(y)) return -1;
    int ans = INF;
    if(depth[x] < depth[y]) {
        std::swap(x, y);
    }

    while(depth[x] > depth[y]) {
        ans = std::min(ans, w[x][lg[depth[x] - depth[y]] - 1]);
        x = f[x][lg[depth[x] - depth[y]] - 1];
    }

    if(x == y) return ans;

    for (int i = lg[depth[x]] - 1; i >= 0; i--) {
        if(f[x][i] != f[y][i]) {
            ans = std::min(ans, std::min(w[x][i], w[y][i]));
            x = f[x][i];
            y = f[y][i];
        }
    }

    ans = std::min(ans, std::min(w[x][0], w[y][0]));
    return ans;
}

int main() {
    memset(head, -1, sizeof(head));
    memset(w, 0x3f, sizeof(w));
    static int m;
    scanf("%d %d", &n, &m);
    init();
    for (int i = 1; i <= m; i++) {
        int x, y, z;
        scanf("%d %d %d", &x, &y, &z);
        g[i].x = x;
        g[i].y = y;
        g[i].z = z;
    }

    std::sort(g + 1, g + 1 + m, cmp);

    for (int i = 1; i <= m; i++) {
        int x = find(g[i].x);
        int y = find(g[i].y);
        if(x == y) continue;

        par[x] = y;
        addedge(g[i].x, g[i].y, g[i].z);
        addedge(g[i].y, g[i].x, g[i].z);
    }

    for (int i = 1; i <= n; i++) {
        if(!Vis[i]) {
            dfs(i, 0);
        }
    }

    for (int i = 1; i <= n; i++) {
        lg[i] = lg[i - 1] + (1 << lg[i - 1] == i);
    }

    static int t;
    scanf("%d", &t);
    for (int i = 1; i <= t; i++) {
        int x, y;
        scanf("%d %d", &x, &y);
        printf("%d
", lca(x, y));
    } return 0;
}
原文地址:https://www.cnblogs.com/Nicoppa/p/11475593.html