一棵树初始只有一个编号为\(1\)的根结点。\(n(n\le3\times10^5)\)次操作,每次新增一个点作为\(p_i\)的子结点,询问更新后有多少点可以作为树直径的端点。
有一个显然的转化就是:有多少个点能作为直径端点 = 有多少点到直径中点距离等于直径长的一半。但直径中点可以在点上,也可以在某条边的中点。但两者本质是一样的,因为你可以往中点所在的边中间加一个点。所以下面只探讨在点上的情况。
假设当前的直径中点为\(rt\),那么\(rt\)会把树分割为若干个子树,假设当前加入的点为\(u\)并且在以\(v\)为根的子树中(被\(rt\)分割出的子树)。设直径长度的一半为\(len\),那么我们考虑三种情况,一种是\(u\)到\(rt\)的距离\(<len\),此时不需要做任何事;如果\(=len\),那么就多了一个能当端点的点;如果\(>len\),那么先前\(v\)子树中所有的答案点都不能作为端点了,那么答案减去这些点的数量,同时让\(u\)成为新的答案点,并且此时直径长度就\(+1\)了,\(rt\)会相应地移动半条边。
所以我们需要做的就是查询并修改每个点子树内的答案点数量,很normal的dfn+线段树。
复杂度\(nlogn\)。
#include<bits/stdc++.h>
#define rg register
#define il inline
#define cn const
#define gc getchar()
#define fp(i,a,b) for(rg int i=(a),ed=(b);i<=ed;++i)
#define fb(i,a,b) for(rg int i=(a),ed=(b);i>=ed;--i)
#define go(u) for(rg int i=head[u];~i;i=e[i].nxt)
using namespace std;
typedef cn int cint;
typedef long long LL;
il int rd(){
rg int x(0),f(1); rg char c(gc);
while(c<'0'||'9'<c){if(c=='-')f=-1;c=gc;}
while('0'<=c&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=gc;
return x*f;
}
cint maxn=3e5+10;
int n,p[maxn],fa[maxn][19],dep[maxn],dfn[maxn],lst[maxn],tot;
struct edge{int to,nxt;}e[maxn];
int head[maxn],k;
il void add(cint &u,cint &v){e[k]=(edge){v,head[u]},head[u]=k++;}
void dfs(int u){
dfn[u]=++tot;
fp(i,1,18)fa[u][i]=fa[fa[u][i-1]][i-1];
go(u)dep[e[i].to]=dep[u]+1,dfs(e[i].to);
lst[u]=tot;
}
int cnt[maxn<<2],tg[maxn<<2];
void ins(int d,int l,int r,cint &des){
if(l==r)return ++cnt[d],void();
if(tg[d])tg[d]=0,cnt[d<<1]=cnt[d<<1|1]=0,tg[d<<1]=tg[d<<1|1]=1;
int md=l+r>>1;
if(des<=md)ins(d<<1,l,md,des);
else ins(d<<1|1,md+1,r,des);
cnt[d]=cnt[d<<1]+cnt[d<<1|1];
}
void clr(int d,int l,int r,cint &dl,cint &dr){
if(dl<=l&&r<=dr)return tg[d]=1,cnt[d]=0,void();
if(tg[d])tg[d]=0,cnt[d<<1]=cnt[d<<1|1]=0,tg[d<<1]=tg[d<<1|1]=1;
int md=l+r>>1;
if(dl<=md)clr(d<<1,l,md,dl,dr);
if(dr>md)clr(d<<1|1,md+1,r,dl,dr);
cnt[d]=cnt[d<<1]+cnt[d<<1|1];
}
int qry(int d,int l,int r,cint &dl,cint &dr){
if(dl<=l&&r<=dr)return cnt[d];
if(tg[d])tg[d]=0,cnt[d<<1]=cnt[d<<1|1]=0,tg[d<<1]=tg[d<<1|1]=1;
int md=l+r>>1,ans=0;
if(dl<=md)ans=qry(d<<1,l,md,dl,dr);
if(dr>md)ans+=qry(d<<1|1,md+1,r,dl,dr);
return ans;
}
il int getlca(int u,int v){
if(dep[u]>dep[v])swap(u,v);
fb(i,18,0)if(dep[fa[v][i]]>=dep[u])v=fa[v][i];
if(u==v)return u;
fb(i,18,0)if(fa[u][i]!=fa[v][i])u=fa[u][i],v=fa[v][i];
return fa[u][0];
}
int len,type,rt=1,rt1,rt2;
il int slv1(cint &u){
rg int lca=getlca(u,rt),dis=dep[u]+dep[rt]-dep[lca]*2;
if(dis==len)ins(1,1,n,dfn[u]);
else if(dis>len){
if(dfn[rt]<=dfn[u]&&dfn[u]<=lst[rt]){
rg int son=u;
fb(i,18,0)if(dep[fa[son][i]]>dep[rt])son=fa[son][i];
clr(1,1,n,dfn[son],lst[son]),ins(1,1,n,dfn[u]);
type=1,rt1=rt,rt2=son;
}else{
if(dfn[rt]>1)clr(1,1,n,1,dfn[rt]-1);
if(lst[rt]<n)clr(1,1,n,lst[rt]+1,n);
ins(1,1,n,dfn[u]);
type=1,rt1=fa[rt][0],rt2=rt;
}
}
return cnt[1];
}
il int slv2(cint &u){//当然你也可以把边拆成点
rg int lca,dis;
if(dfn[rt2]<=dfn[u]&&dfn[u]<=lst[rt2]){
lca=getlca(u,rt2);
dis=dep[u]+dep[rt2]-dep[lca]*2;
}else{
lca=getlca(u,rt1);
dis=dep[u]+dep[rt1]-dep[lca]*2;
}
if(dis==len)ins(1,1,n,dfn[u]);
else if(dis>len){
if(dfn[rt2]<=dfn[u]&&dfn[u]<=lst[rt2]){
clr(1,1,n,dfn[rt2],lst[rt2]),ins(1,1,n,dfn[u]);
type=0,rt=rt2,++len;
}else{
if(dfn[rt2]>1)clr(1,1,n,1,dfn[rt2]-1);
if(lst[rt2]<n)clr(1,1,n,lst[rt2]+1,n);
ins(1,1,n,dfn[u]);
type=0,rt=rt1,++len;
}
}
return cnt[1];
}
int main(){
memset(head,-1,sizeof head);
n=rd()+1;
fp(i,2,n)p[i]=fa[i][0]=rd(),add(p[i],i);
dfs(1),ins(1,1,n,1);
fp(i,2,n){
if(type==0)printf("%d\n",slv1(i));
else printf("%d\n",slv2(i));
}
return 0;
}