树上路径(动态树直径)
树上路径
Description
一棵树的构造过程为:首先以1号点为根,然后依次加入2~n号点。
加入i号点时,在1~i-1点中选择一个点为f[i],将i号点与其相连接。
Yuri想要求出,每次加点之后路上的最长路径长度
Hint
动态求树的直径
记录下现在直径的两个端点i,j,加入一个点s时,如果可以更新直径,那么新直径一定是(s,j)或(s,i)
可以想当然的证明,如果是s和另一点t构成(s,t)为新直径的话,(i,j)一定不会是原树直径
所以求一下dist(s,i)和dist(s,j),如果大于直径就更新
求dist就用倍增求LCA算树上路径就好了
Code
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=200010;
int ans,n,mx,x,top;
int dep[N],f[N][20],v[N],stk[N];
int LCA(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=18;~i;i--)
if(dep[f[x][i]]>=dep[y]) x=f[x][i];
if(x==y) return x;
for(int i=18;~i;i--)
if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
}
int main(){
scanf("%d",&n);
dep[1]=1; top=stk[1]=v[1]=1;
for(int i=2;i<=n;i++){
scanf("%d",&f[i][0]);
for(int j=1;j<=18;j++)
f[i][j]=f[f[i][j-1]][j-1];
dep[i]=dep[f[i][0]]+1;
if(v[f[i][0]]){
ans++; v[i]=1;
for(;top;top--) v[stk[top]]=0;
stk[++top]=i;
}
else {
x=LCA(stk[1],i);
ans=max(dep[i]+dep[stk[1]]-2*dep[x],ans);
if(dep[i]==dep[stk[1]]) v[stk[++top]=i]=1;
}
printf("%d ",ans);
}
return 0;
}