十四届蓝桥杯C/C++B组 I题 倍增LCA 带权

十四届蓝桥杯C/C++B组 I题 倍增LCA 带权

考场写的地杰斯特拉暴力,太捞了。

LCA模板题,GG。

OIwiki地址

模板题地址 HDU2586

通过计算LCA算出最近公共祖先,然后通过:
d i s t [ u ] + d i s t [ v ] − 2 ∗ d i s t [ l c a ( u , v ) ] ; dist[u] + dist[v] - 2 * dist[lca(u,v)]; dist[u]+dist[v]2dist[lca(u,v)];
计算树上每两个点的距离,先将路上所有景点的路径之和加起来,然后在遍历每一个要删除的点,删除的话,总和先减去前一个和后一个的距离,加上前一个和后一个的最短路径。具体实现看代码。

代码:

#include 
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 5;
const int LOGN = 20;

int n, m;
int depth[MAXN], parent[MAXN][LOGN], dist[MAXN];
vector<pair<int, int>> adj[MAXN];

void dfs(int u, int p, int d) {
    depth[u] = depth[p] + 1;
    parent[u][0] = p;
    dist[u] = d;
    for (auto &x : adj[u]) {
        int v = x.first, w = x.second;
        if (v == p) continue;
        dfs(v, u, d + w);
    }
}

void preprocess() {
    for (int j = 1; j < LOGN; j++) {
        for (int i = 1; i <= n; i++) {
            if (parent[i][j - 1] != -1) {
                parent[i][j] = parent[parent[i][j - 1]][j - 1];
            }
        }
    }
}

int lca(int u, int v) {
    if (depth[u] < depth[v]) swap(u, v);
    int diff = depth[u] - depth[v];
    for (int j = LOGN - 1; j >= 0; j--) {
        if (diff & (1 << j)) {
            u = parent[u][j];
        }
    }
    if (u == v) return u;
    for (int j = LOGN - 1; j >= 0; j--) {
        if (parent[u][j] != parent[v][j]) {
            u = parent[u][j];
            v = parent[v][j];
        }
    }
    return parent[u][0];
}

int distance(int u, int v) {
    int p = lca(u, v);
    return dist[u] + dist[v] - 2 * dist[p];
}

int main() {
    cin >> n >> m;
    for (int i = 1; i < n; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        adj[u].push_back({v, w});
        adj[v].push_back({u, w});
    }
    dfs(1, 0, 0);
    preprocess();
    ll sum = 0;
    vector<int> a(m);
    for (int i = 0; i < m; i++) {
        cin >> a[i];
        if (i) sum += distance(a[i - 1], a[i]);
    }
    // cout << sum << endl;

    for (int i = 0; i < m; i++) {  // 时间复杂度 O(mlogn)
        int ans = sum;
        if (i) ans -= distance(a[i - 1], a[i]);
        if (i + 1 < m) ans -= distance(a[i], a[i + 1]);
        if (i && i + 1 < m) ans += distance(a[i - 1], a[i + 1]);
        cout << ans << " ";
    }
    return 0;
}

你可能感兴趣的:(蓝桥杯,c语言,c++)