P4281 [AHOI2008] 枚举 + 倍增 LCA

题意

传送门 P4281 [AHOI2008]紧急集合 / 聚会

题解

三人中任意两人 x , y x,y x,y 聚合,路径唯一,即 d i s t ( x , l c a ( x , y ) ) d i s t + d i s t ( y , l c a ( x , y ) ) dist(x,lca(x,y))dist+dist(y,lca(x,y)) dist(x,lca(x,y))dist+dist(y,lca(x,y)),则 x , y x,y x,y 走的路径应该覆盖上述路径。使集合花费最小,则应该避免出现多个人走相同路径的情况,即某两个人聚合后,应该保持在原地,等第三个人前来聚合。

考虑枚举最终聚合地点。聚合地点的备选位置为任意两人 x , y x,y x,y 间路径上的节点 p p p,枚举量过大;对于任意节点 p p p,其选取并不影响 x , y x,y x,y 两人的花费和,考虑最小化第三个人 z z z 的花费。若 z z z 不在 l c a ( x , y ) lca(x,y) lca(x,y) 为根的子树中, p p p l c a ( x , y ) lca(x,y) lca(x,y);反之, p p p l c a ( z , x ) lca(z,x) lca(z,x) l c a ( z , y ) lca(z,y) lca(z,y)。于是得到算法,枚举所有两两节点的 L C A LCA LCA,共 C 3 2 \text{C}_{3}^{2} C32 个,以其为最终汇聚点求解路径花费,更新答案。

倍增求解 L C A LCA LCA,总时间复杂度 O ( ( N + M ) log ⁡ N ) O((N+M)\log N) O((N+M)logN)

#include <bits/stdc++.h>
using namespace std;
const int maxn = 500005, maxlg = 20, inf = 0x3f3f3f3f;
int N, M, pos, res, lg[maxn], fa[maxn][maxlg], dep[maxn];
int tot, head[maxn], to[maxn << 1], nxt[maxn << 1];

inline void add(int x, int y) {
    
     to[++tot] = y, nxt[tot] = head[x], head[x] = tot; }

void dfs(int x, int f, int d)
{
    
    
    fa[x][0] = f, dep[x] = d;
    for (int k = 1; k <= lg[d]; ++k)
        fa[x][k] = fa[fa[x][k - 1]][k - 1];
    for (int i = head[x]; i; i = nxt[i])
    {
    
    
        int y = to[i];
        if (y != f)
            dfs(y, x, d + 1);
    }
}

int lca(int x, int y)
{
    
    
    if (dep[x] < dep[y])
        swap(x, y);
    while (dep[x] > dep[y])
        x = fa[x][lg[dep[x] - dep[y]]];
    if (x == y)
        return x;
    for (int k = lg[dep[x]]; k >= 0;)
        if (fa[x][k] != fa[y][k])
            x = fa[x][k], y = fa[y][k], k = lg[dep[x]];
        else
            --k;
    return fa[x][0];
}

void upd(int x, int y, int z)
{
    
    
    int p = lca(x, y), a = dep[x] + dep[y] - (dep[p] << 1);
    int q = lca(p, z), b = dep[p] + dep[z] - (dep[q] << 1);
    if (a + b < res)
        pos = p, res = a + b;
}

int main()
{
    
    
    scanf("%d%d", &N, &M);
    for (int i = 1, x, y; i < N; ++i)
        scanf("%d%d", &x, &y), add(x, y), add(y, x);
    lg[0] = -1;
    for (int i = 1; i < N; ++i)
        lg[i] = lg[i - 1] + ((1 << (lg[i - 1] + 1)) == i);
    dfs(1, 0, 0);
    while (M--)
    {
    
    
        int x, y, z;
        scanf("%d%d%d", &x, &y, &z);
        res = inf;
        upd(x, y, z), upd(y, z, x), upd(z, x, y);
        printf("%d %d\n", pos, res);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/neweryyy/article/details/114700804