蒟蒻的倍增(LCA)

蒟蒻的倍增

今天蒟蒻蒻学了倍增
倍增,字面意思
1,2,4,8,16,32 … …
也就是一倍一倍地增加(曲解qwq)
说到倍增,要从一个看似和倍增没有关系的题开始讲起
洛谷的LCA模板题(LCA:最近公共祖先)
附上链接: https://www.luogu.com.cn/problem/P3379
看完题目再食用效果更佳——鲁迅
以蒟蒻的智商来看这道题,首先想到的肯定就是暴力了
先用一个fa[]数组把这棵树存起来
然后对于每一对需要检测的节点,
让他们一次一次往上找
直到找到那个最近的公共节点
结果注定TLE
更加难受的是,如果所求两点i,j
i为j的祖先的时候,得到的答案是i的祖先而不是j的

#include<bits/stdc++.h>
using namespace std;
int fa[500005];
int father(int x)
{
    while(x!=fa[x])
    {
        x=fa[x];
    }
    return x;
}
int main()
{
    int n,m,s;
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<=s;i++) fa[i]=i;
    for(int i=1;i<=n-1;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        fa[a]=b;
    }
    for(int i=1;i<=m;i++)
    {
        int aa,bb;
        scanf("%d%d",&aa,&bb);
        while(1)
        {
            if(aa!=bb)
            {
                aa=fa[aa];
                bb=fa[bb];
            }
            else break;
        }
        printf("%d\n",aa);
    }
    return 0;
}
///原来的思路(暴力)

这样就需要用到倍增法优化的LCA了,需要用到链式前向星存树,因为树无向,所以要正反存两次,结构体数组也要开两倍。
edge,head,top都是存树用的
s为结点数
pp是倍增数组,有种dp的感觉
pp[i][j]=x中,i为当前节点,j为从i结点向上走2^j,x为所到达的节点
d存的是当前节点的深度

#include<bits/stdc++.h>
using namespace std;
const int N=500000+5;
struct node
{
    int to,next;
}edge[N<<1];

int head[N];
int top=0;
int n,m,s;
int pp[N][22];///一般倍增到1<<20就够用了
int d[N];
int insert_edge(int x,int y)
{
    edge[top].next=head[x];
    edge[top].to=y;
    head[x]=top++;
}///存图

void dfs(int now,int fa)///
{
    d[now]=d[fa]+1;
    pp[now][0]=fa;
    for(int i=1;(1<<i)<=d[now];i++)
    {
        pp[now][i]=pp[pp[now][i-1]][i-1];
    }
    for(int i=head[now];i!=-1;i=edge[i].next)
    {
        int to=edge[i].to;
        if(to!=fa)
            dfs(to,now);
    }
}

int lca(int a,int b)
{
    if(d[a]>d[b])
        swap(a,b);
    for(int i=20;i>=0;i--)///一定要从大往小找
    {
        if(d[a]<=d[b]-(1<<i))
        {
            b=pp[b][i];
        }
    }
    if(a==b) return a;
    for(int i=20;i>=0;i--)
    {
        if(pp[a][i]==pp[b][i])
            continue;
        else
        {
            a=pp[a][i];
            b=pp[b][i];
        }
    }
    return pp[a][0];
}

int main()
{
    scanf("%d%d%d",&n,&m,&s);
    memset(head,-1,sizeof(head));
    for(int i=1;i<=n-1;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        insert_edge(a,b);
        insert_edge(b,a);
    }
    dfs(s,0);
    while(m--)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        printf("%d\n",lca(a,b));
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值