cumtoj修建道路 LCA倍增

题目描述
nn个点n1n-1条边,点之间两两连通,每次询问33个点,求使这33个点连通的最小花费。(1n,q1051\leq n,q\leq 10^5

因为给的是一颗树,最终的答案就是dist(x,y)+dist(y,z)+dist(x,z)2.\frac{dist(x,y)+dist(y,z)+dist(x,z)}{2}.
倍增求lca即可。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<stack>
#include<cstdlib>

#define ll long long
#define mem(a, x) memset(a,x,sizeof(a))
#define iosup ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
const int N = 1e5 + 10;
int fa[N][1 << 5];
int n, q, dep[N];
ll dis[N];
struct Edge {
    int v, w;
};
vector<Edge> G[N];

void dfs(int u, int father) {
    fa[u][0] = father;
    for (int i = 1; i <= 20; i++)
        fa[u][i] = fa[fa[u][i - 1]][i - 1];
    for (Edge &e:G[u])
        if (e.v != father) {
            dis[e.v] = dis[u] + e.w;
            dep[e.v] = dep[u] + 1;
            dfs(e.v, u);
        }
}
inline void init() {
    scanf("%d%d", &n, &q);
    int u, v, c;
    for (int i = 1; i < n; i++) {
        scanf("%d%d%d", &u, &v, &c);
        G[u].push_back(Edge{v, c});
        G[v].push_back(Edge{u, c});
    }
    dis[1] = dep[1] = 0;
    dfs(1, 0);
}
int lca(int u,int v){
    if (dep[u]>dep[v]) std::swap(u,v);
    int k = dep[v] - dep[u];
    for(int i=20;i>=0;i--){
        if((k>>i) & 1)
            v = fa[v][i];
    }
    if (u==v) return u;
    for(int i=20;i>=0;i--){
        if (fa[u][i]!=fa[v][i]){
            u=fa[u][i];
            v=fa[v][i];
        }
    }
    return fa[u][0];
}
ll d(int u,int v){
    return dis[u] + dis[v] - 2*dis[lca(u,v)];
}
int main() {
    init();
    int x, y, z;
    for (int i = 1; i <= q; i++) {
        scanf("%d%d%d", &x, &y, &z);
        printf("%lld\n",(d(x,y)+d(y,z)+d(x,z)) >> 1);
    }
    return 0;
}
posted @ 2019-08-16 16:07  Mr.doublerun  阅读(16)  评论(0)    收藏  举报