ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

树上倍增求树中任意两点

2022-01-30 11:58:33  阅读:153  来源: 互联网

标签:return 两点 min int 求树中 fa depth ans 倍增


跳步祖宗数组 fa : fa [ i ] [ 20 ] 表示从当前位置向上跳 2^ j 次方步后所在的点的代号。

深度数组 depth[ i ] : 表示当前的点的深度。(用于比较两点位置。

跳步祖宗数组需要预处理:(dfs

for(int i = 1; i <= n; i ++)
{
    if(!st[i])  // 挨个, 防止有不连通的块没有遍历到;
    {
        depth[i] = 1;
        dfs(i);
    }
}

void dfs(int x)
{
    st[x] = true;
    for(int i = h[x]; i != -1; i = ne[i])
    {
        int y = e[i];
        if(b[y]) continue;
        st[y] = true;
        depth[y] = dep[x] + 1;
        f[y][0] = x; // y 的 2 的 0 次方个祖宗是 x
        dfs(y);
    }
    return;
}

对于跳步祖宗数组 :

递推关系: fa[ i ] [ j ] = fa [ f [ i ] [ j - 1] ]  [ j - 1] ; ( i 的第 2 ^ j  次方个祖宗 是  i 的第 2 ^ j - 1 个祖宗的 第 2 ^ j - 1 个祖宗。

例题: [NOIP2013]货车运输 - 题库 - 计蒜客 (jisuanke.com)

lca 部分:

lca(int x, int y)
{
    if(find(x) != find(y)) return -1; // 不连通。
    int ans = inf;
    if(depth[x] > depth[y]) swap(x, y); // 先处理更深的。
    for(int i = 20; i >= 0; i -- ) // 从远处开始跳。
    {
        if(depth[fa[y][i]] >= depth[x])
        {
            ans = min(ans, min[y][i]);
            y = fa[y][i];
        }
    }
    if(x == y) return ans;
    for(int i = 20; i >= 0; i -- )
    {
        if(fa[x][y] != fa[y][i]) // 同时跳。
        {
            ans = min(ans, min(minn[x][i], minn[y][i]));
            x = fa[x][i];
            y = fa[y][i];
        }
    }
    return ans;
}

ac:

int h[N], ne[N], e[N], idx;
int w[N];
int f[N];
int fa[N];
int depth[N];

struct edges
{
    int x, y, w;
}edge[N];

bool cmp(const edges &a, const edges &b)
{
    return a.w > b.w;
}

void kruskal()
{
    sort(edge + 1, edge + 1 + m, cmp);
    for(int i = 1; i <= n; i ++)
    f[i] = i;
    for(int i = 1; i <= m; i ++)
    {
        int xx = edge[i].x, yy = edge[i].y;
        int ww = edge[i].w;
        if(find(xx) != find(yy))
        {
            f[find(xx)] = find(yy);
            add(xx, yy, ww);
            add(yy, xx, ww);
        }
    }
    return;
}

void dfs(int x)
{
    st[x] = true;
    for(int i = h[x]; i != -1; i = ne[i])
    {
        int y = e[i];
        if(st[y]) continue;
        st[y] = true;
        depth[y] = depth[x] + 1;
        fa[y][0] = x;
        minn[y][0] = w[i];
        dfs(y);
    }
    return;
}

lca(int x, int y)
{
    if(find(x) != find(y)) return -1; // 不连通。
    int ans = inf;
    if(depth[x] > depth[y]) swap(x, y); // 先处理更深的。
    for(int i = 20; i >= 0; i -- ) // 从远处开始跳。
    {
        if(depth[fa[y][i]] >= depth[x])
        {
            ans = min(ans, min[y][i]);
            y = fa[y][i];
        }
    }
    if(x == y) return ans;
    for(int i = 20; i >= 0; i -- )
    {
        if(fa[x][y] != fa[y][i]) // 同时跳。
        {
            ans = min(ans, min(minn[x][i], minn[y][i]));
            x = fa[x][i];
            y = fa[y][i];
        }
    }
    return ans;
}

void solve()
{
    cin >> n >> m;
    memset(h, -1, sizeof h);
    for(int i = 1; i <= m; i ++)
    {
        scanf("%d%d%d", &edge[i].x, &edge[i].y, &edge[i].w);
    }
    kruskal();
    for(int i = 1; i <= n; i ++)
    {
        if(!st[i])
        {
            depth[i] = 1;
            dfs(i);
        }
    }
    for(int i = 1; i <= 20; i ++)
    for(int j = 1; j <= n; j ++)
    {
        fa[j][i] = fa[fa[j][i - 1]][i - 1];
        minn[j][i] = min(min[[j][i - 1]][i - 1], min[j][i]);
    }
    cin >> q;
    while(q --)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        printf("%d\n", lca(x, y));
    }
return;
}

标签:return,两点,min,int,求树中,fa,depth,ans,倍增
来源: https://blog.csdn.net/m0_61693002/article/details/122751262

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有