分析
先离线存边,建立关系,存好深度。
然后依次处理每个插入操作。保存当前直径的两端分别为s和t,设当前插入的节点为x,在s到t,s到x,t到x三条路径中找到一段最长的路径作为新的直径即可。
树上两点的最短路径长度:两点距离=两个点分别到根的距离和-两倍的lca到根的距离
求树上两点间的最短路径长度可以预处理每个点的深度,然后转化为LCA问题。求LCA可以用倍增实现。建树是O(n)的,倍增的时间复杂度为O(nlogn),因此最后总的时间复杂度为O(nlogn)。
思想上是有贪心的。
上代码
为什么这题我能调这么久¿¿¿
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,cnt,s,t,ans;
int dep[200010],f[200010][30],a[100010];
int lca(int x,int y) //这里的写法有一点变动
{
if(dep[x]<dep[y]) swap(x,y);
int c=dep[x]-dep[y],j=25,t=1<<j;
while(c) //变到同一深度
{
if(c>=t)
{
c-=t;
x=f[x][j];
}
t/=2;
j--;
}
if(x==y) return x;
j=25;
while(j>=0) //同时向上,找到祖先
{
if(f[x][j]!=f[y][j])
{
x=f[x][j];
y=f[y][j];
}
j--;
}
return f[x][0];
}
int main()
{
scanf("%d",&n);
cnt=4;
f[2][0]=f[3][0]=f[4][0]=1;
dep[2]=dep[3]=dep[4]=1;
s=2,t=3,ans=2;//直径的一端,另一端,长度
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
f[++cnt][0]=f[++cnt][0]=a[i]; //父节点
dep[cnt-1]=dep[cnt]=dep[a[i]]+1; //维护深度
a[i]=cnt;
}
for(int j=1;j<=25;j++)
{
for(int i=1;i<=cnt;i++)
{
f[i][j]=f[f[i][j-1]][j-1]; //倍增预处理f数组
}
}
for(int i=1;i<=n;i++)
{
int x=a[i];
int xs=lca(x,s),xt=lca(x,t);
int ans1=dep[x]+dep[s]-2*dep[xs];
int ans2=dep[x]+dep[t]-2*dep[xt];
/*两点距离=两个点分别到根的距离和-两倍的lca到根的距离*/
if(ans1>ans&&ans1>=ans2) //更新答案
{
ans=ans1;
t=x;//更新直径的两端点
}
if(ans2>ans)
{
ans=ans2;
s=x;
}
printf("%d\n",ans);
}
return 0;
}
UPD:新的LCA写法,感觉更好,模板(luoguP3379)贴上:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,s,fa[500001][30],dep[500001];
int tot,hd[500001];
struct node
{
int to,next;
}e[1000001];
void add(int x,int y)
{
e[++tot]=(node){y,hd[x]};
hd[x]=tot;
}
void dfs(int x,int fx)
{
fa[x][0]=fx;
dep[x]=dep[fx]+1;
for(int i=1;i<=20;i++)
{
fa[x][i]=fa[fa[x][i-1]][i-1];
/*意思是now的2^i祖先等于now的2^(i-1)祖先的2^(i-1)祖先
2^i=2^(i-1)+2^(i-1)*/
}
for(int i=hd[x];i>0;i=e[i].next)//防止找回他的父亲
{
if(e[i].to!=fx) dfs(e[i].to,x);
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=20;i>=0;i--)
{
if(dep[fa[x][i]]>=dep[y])
{
x=fa[x][i];
}
}
if(x==y) return x;
for(int i=20;i>=0;i--)
{
if(fa[x][i]!=fa[y][i])
{
x=fa[x][i];
y=fa[y][i];
}
}
return fa[x][0];//0次方就是直接父亲
}
int main()
{
cin>>n>>m>>s;
for(int i=1;i<=n-1;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs(s,0);
for(int i=1;i<=m;i++)
{
int x,y;
cin>>x>>y;
cout<<lca(x,y)<<endl;
}
return 0;
}