树上倍增法,LCA——CF-832-D

题目含义

求树上两个点到第三个点重合路径的最大长度

题目分析

设三个点为A,B,C

dis(A,B)=deep[a]+deep[b]-2*deep[lca(A,B)]

所以这是个求最近公共祖宗的题

那么答案可以由两种方法求

(1)若以C为第三个点,求( dis(a,c)+dis(b,c)-dis(a,b) )/2 然后三个点分别三种情况求最大

(2)求出三个点两两的公共祖先,取深度最大的点,然后求这点到三个点的最长距离

题目代码

#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
const int maxn=1e5+7;
int n,q,a,b,c,tot;
int head[maxn],father[maxn][20],deep[maxn];
struct node{
    int to,next;
}edge[maxn*2];
void add(int u,int v){
    edge[tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}
void dfs(int u,int fa,int d){
    deep[u]=d;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int to=edge[i].to;
        if(to==fa)continue;
        dfs(to,u,d+1);
        father[to][0]=u;
    }
}
void InitICA(){
    for(int j=1;j<20;j++)
    for(int i=1;i<=n;i++){
        father[i][j]=father[father[i][j-1]][j-1];
    }
}
int lca(int x,int y){
    if(deep[x]<deep[y])swap(x,y);
    for(int i=19;i>=0;i--){
        if(deep[father[x][i]]>=deep[y])x=father[x][i];
    }
    if(x==y)return x;
    for(int i=19;i>=0;i--){
        if(father[x][i]!=father[y][i]){
            x=father[x][i];
            y=father[y][i];
        }
    }
    return father[x][0];
}
int dis(int x,int y){
    return deep[x]+deep[y]-2*deep[lca(x,y)];
}
int main(){
    scanf("%d%d",&n,&q);
    memset(head,-1,sizeof(head));
    tot=0;
    for(int i=2;i<=n;i++){
        scanf("%d",&a);
        add(a,i),add(i,a);
    }
    dfs(1,0,0);
    InitICA();
    while(q--){
        scanf("%d%d%d",&a,&b,&c);
        int ans=0;
        ans=max(ans,(dis(a,c)+dis(b,c)-dis(a,b))/2);
        ans=max(ans,(dis(a,b)+dis(b,c)-dis(a,c))/2);
        ans=max(ans,(dis(a,c)+dis(a,b)-dis(b,c))/2);
        printf("%d\n",ans+1);
    }
    return 0;
}
View Code

我是用的第一种方法

第二种可以看这位博主写的

链接

猜你喜欢

转载自www.cnblogs.com/helman/p/11272997.html