LCA板子

自用板子

```
//最近公共祖先(LCA)//
struct Edge { int to, next; }edge[2 * N];     //链式前向星
int head[2 * N], cnt;
void init()      //链式前向星:初始化
{
    for (int i = 0; i < 2 * N; ++i) { edge[i].next = -1;   head[i] = -1; }
    cnt = 0;
}
void addedge(int u, int v)        //链式前向星:加边
{
    edge[cnt].to = v;  edge[cnt].next = head[u];  head[u] = cnt++;
}       
//以上是链式前向星
int fa[N][20], deep[N];
void dfs(int x, int father)          //求x的深度deep[x]和fa[x][]。father是x的父结点。
{        
    deep[x] = deep[father] + 1;      //深度:比父结点深度多1
    fa[x][0] = father;               //记录父结点
    for (int i = 1; (1 << i) <= deep[x]; i++)    //求fa[][]数组,它最多到根结点
        fa[x][i] = fa[fa[x][i - 1]][i - 1];
    for (int i = head[x]; ~i; i = edge[i].next)  //遍历结点i的所有孩子。~i可写为i!=-1
        if (edge[i].to != father)                //邻居:除了父亲,都是孩子
            dfs(edge[i].to, x);
}
int LCA(int x, int y)
{
    if (deep[x] < deep[y])  swap(x, y);     //让x位于更底层,即x的深度值更大
    //(1)把x和y提到相同的深度
    for (int i = 19; i >= 0; i--)           //x最多跳19次:2^19 > 500005
        if (deep[x] - (1 << i) >= deep[y])  //如果x跳过头了就换个小的i重跳
            x = fa[x][i];                   //如果x还没跳到y的层,就更新x继续跳
    if (x == y)  return x;                  //y就是x的祖先
    //(2)x和y同步往上跳,找到LCA
    for (int i = 19; i >= 0; i--)           //如果祖先相等,说明跳过头了,换个小的i重跳
        if (fa[x][i] != fa[y][i])           //如果祖先不等,就更新x、y继续跳
        {      
            x = fa[x][i];   y = fa[y][i];
        }
    return fa[x][0];                        //最后x位于LCA的下一层,父结点fa[x][0]就是LCA
}
int main()
{
    init();                                  //初始化链式前向星
    int n, m, root;  scanf("%d%d%d", &n, &m, &root);
    for (int i = 1; i < n; i++)              //读一棵树,用链式前向星存储
    {
        int u, v;       scanf("%d%d", &u, &v);
        addedge(u, v);  addedge(v, u);
    }
    dfs(root, 0);                            //计算每个结点的深度并预处理fa[][]数组
    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、付费专栏及课程。

余额充值