十四届蓝桥杯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 <bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
const int MAXN = 2e5 + 5;
const int LOGN = 30;

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];
}

signed 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)
        ll 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;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

万伏小太阳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值