题目含义
求树上两个点到第三个点重合路径的最大长度
题目分析
设三个点为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
我是用的第一种方法
第二种可以看这位博主写的