第三次周赛D题

题意:

多次询问树上两点最短 距离。

TLE做法:对每次询问跑一遍最短路,用堆优化迪杰斯特拉的话复杂度大概为\(O(q*(n+m)logm)\)
\(100\)分做法:
用一个\(dis\)数组记录根节点\(root\)到每一个节点的距离,那么树上两点\(u,v\)的距离就是\(root\)\(u\)的距离加上\(root\)\(v\)的距离减去两倍的\(root\)\(lca(u,v)\)的距离(可以画下图感性理解...),即
\(ans=dis[u]+dis[v]-2*dis[lca(u,v)]\)
我用的是树上倍增求\(lca\)

最近公共祖先(\(lca\))是指在一个树或者有向无环图中同时拥有\(v\)\(w\)作为后代的最深的节点,最近公共祖先是两个节点所有公共祖先中离根节点最远的。

其实就是\(lca\)模板题,看几篇博客就会了。
具体求法可以参考一下大佬的博客浅谈最近公共祖先
最近公共祖先

#include <bits/stdc++.h>
using namespace std;

#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define endl '\n'

typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> pii;

const int maxn  = 75000 + 5;
const int maxm  = 100 + 5;
const int inf   = 0x3f3f3f3f;
const LL  mod   = 1e9 + 7;//19260817
const double pi = acos(-1.0);

int n, q, cnt, x, y, deep[maxn], pre[maxn][20], head[maxn], dis[maxn];

struct node {
    int v, w, next;
} edge[maxn << 1];

void addedge(int u, int v, int w) {
    edge[++cnt].v = v;
    edge[cnt].w = w;
    edge[cnt].next = head[u];
    head[u] = cnt;
}

void dfs(int x, int f) {
    pre[x][0] = f;
    for(int i = head[x]; i; i = edge[i].next) {
        if(edge[i].v != f) {
            deep[edge[i].v] = deep[x] + 1;
            dis[edge[i].v] = dis[x] + edge[i].w;
            dfs(edge[i].v, x);
        }
    }
}

void solve() {
    for(int j = 1; j <= 19; j++)
        for(int i = 1; i <= n; i++)
            pre[i][j] = pre[pre[i][j - 1]][j - 1];
}

int lca(int u, int v) {
    if(deep[u] < deep[v]) swap(u, v);
    int dc = deep[u] - deep[v];
    for(int i = 0; i <= 19; i++) {
        if((1 << i) & dc) u = pre[u][i];
    }
    if(u == v) return u;
    for(int i = 19; ~i; i--) {
        if(pre[u][i] != pre[v][i]) {
            u = pre[u][i];
            v = pre[v][i];
        }
    }
    return pre[u][0];
}

int main() {
    scanf ("%d", &n);
    for (int i = 1, u, v, w; i <= n - 1; i++) {
        scanf ("%d %d %d", &u, &v, &w);
        ++u, ++v;
        addedge(u, v, w), addedge(v, u, w);
    }
    dfs(1, -1);
    solve();
    scanf ("%d", &q);
    while (q--) {
        scanf ("%d %d", &x, &y);
        ++x, ++y;
        printf ("%d\n", dis[x] + dis[y] - 2 * dis[lca(x, y)]);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ChaseNo1/p/11366608.html
今日推荐