NC23482 小A的最短路(LCA)

题目链接

题意:
有 一 棵 n 个 点 的 树 有一棵n个点的树 n
n − 1 条 边 经 过 每 条 边 都 需 要 1 点 体 力 n-1条边经过每条边都需要1点体力 n11
给 定 一 组 u , v 可 以 从 u 到 v 或 者 v 到 u 不 需 要 花 费 体 力 给定一组u,v可以从u到v或者v到u不需要花费体力 u,vuvvu
q 次 询 问 , x 到 y 最 少 花 费 体 力 q次询问,x到y最少花费体力 qxy
题解:
n < = 3 e 5 , q < = 1 e 6 n<=3e5,q<=1e6 n<=3e5,q<=1e6
树 上 多 次 询 问 距 离 , L C A 的 经 典 题 目 树上多次询问距离,LCA的经典题目 LCA
预 处 理 每 个 点 的 深 度 和 他 们 的 L C A 预处理每个点的深度和他们的LCA LCA
距 离 就 是 两 点 深 度 和 减 去 他 们 L C A 深 度 的 二 倍 距离就是两点深度和减去他们LCA深度的二倍 LCA
直 接 求 x − > y 的 距 离 直接求x->y的距离 x>y
然 后 求 x − > u − > v − > y 可 能 可 以 减 少 花 费 然后求x->u->v->y可能可以减少花费 x>u>v>y
求 x − > v − > u − > y 求x->v->u->y x>v>u>y
找 最 小 值 就 是 答 案 , 每 次 都 是 一 个 l o g 复 杂 度 的 查 询 找最小值就是答案,每次都是一个log复杂度的查询 log
AC代码

/*
    Author : zzugzx
    Lang : C++
    Blog : blog.csdn.net/qq_43756519
*/
#include<bits/stdc++.h>
using namespace std;

#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(), (x).end()
#define endl '\n'
#define SZ(x) (int)x.size()
#define mem(a, b) memset(a, b, sizeof(a))

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int mod = 1e9 + 7;
//const int mod = 998244353;

const double eps = 1e-6;
const double pi = acos(-1.0);
const int maxn = 1e6 + 10;
//const int N = 1e5 + 5;
const ll inf = 0x3f3f3f3f;
const int dir[][2]={{0, 1}, {1, 0}, {0, -1}, {-1, 0}, {1, 1}, {1, -1}, {-1, 1}, {-1, -1}};
int n,depth[maxn], f[maxn][50];
int from[maxn], to[maxn << 1], nxt[maxn << 1], cnt,Log[maxn];
//链式前向星加边
void addEdge (int u, int v) {
    to[++cnt] = v, nxt[cnt] = from[u], from[u] = cnt;
}
//计算深度&计算祖先
void dfs (int u, int fa) {
    depth[u] = depth[fa] + 1;
    for (register int i = 1; i <= Log[n]; ++i) {
        if ((1 << i) > depth[u]) break;
        f[u][i] =  f[f[u][i - 1]][i - 1];
    }
    for (register int i = from[u]; i; i = nxt[i]) {
        ll v = to[i];
        if (v == fa) continue;
        f[v][0] = u;
        dfs (v, u);
    }
}
//计算LCA
inline int LCA (int x, int y) {
    if (depth[x] < depth[y]) swap(x, y);
    //我们默认x为更深的那个点
    for(register int i = Log[n] ; i >= 0 ; --i)
		if(depth[x] - (1 << i) >= depth[y]) x = f[x][i];
    //将x跳到和y同一深度上
    if (x == y) return x;
    for (register int i = Log[n]; i >= 0; --i)
        if (f[x][i] != f[y][i])
            x = f[x][i], y = f[y][i];
    //一起向上跳
    return f[x][0];
    //不难看出,此时两个点均在其LCA的下方,往上跳一次即可
}
void init(){
	Log[0] = -1;
	cnt = 0;
	for(int i = 1; i <= n; i++){
        from[i] = 0,depth[i] = 0;
        for(int j = 0; j < 50; j++)
            f[i][j] = 0;
	}
    for (register int i = 1, u, v; i < n; ++i) {
        cin >> u >> v;
        addEdge (u, v); addEdge(v, u);
        Log[i] = Log[i >> 1] + 1;
    }
    Log[n] = Log[n >> 1] + 1;
    dfs(1,0);
}
int dis(int p , int q){return depth[p] + depth[q] - 2 * depth[LCA(p , q)];}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
//  freopen("in.txt", "r", stdin);
//  freopen("out.txt", "w", stdout);
    cin >> n;
    init();
    int u, v;
    cin >> u >> v;
    int _;
    cin >> _;
    while (_--) {
        int x, y;
        cin >> x >> y;
        int ans = dis(x, y);
        ans = min(ans, dis(x, u) + dis(v, y));
        ans = min(ans, dis(x, v) + dis(u, y));
        cout << ans << endl;
    }
    return 0;
}

©️2020 CSDN 皮肤主题: 游动-白 设计师:上身试试 返回首页