传送门
解析:
有一点想法,考虑每条边被经过的次数,假设这条边将树给分为两个大小为 x , y x,y x,y的联通块,则这条边的最大贡献为 min ( x , y ) ∗ 2 \min(x,y)*2 min(x,y)∗2,显然我们考虑给每个点尽量选择在另一边的点就行了。
那么能不能让每条边的贡献达到可能的最大值呢?是可以的。
发现我们算的 min ( x , y ) \min(x,y) min(x,y)实际上就是以重心为根的时候,改变连向的儿子的子树大小。
考虑如下构造:
选取重心为根,可以发现每个
p
[
i
]
p[i]
p[i]都一定可以选择与
i
i
i不在同一个子树中的点,因为每一个子树
s
i
z
<
=
t
o
t
a
l
/
2
siz<=total/2
siz<=total/2。
那么每条边都会被它子树中的点的路径穿过,贡献被最大化。
发现总的答案就是所有点到重心的距离之和。
我们需要动态维护重心,可以上LCT。
不过直接用DFS序+树状数组维护所有子树中被激活了的点的个数就行了,显然加入每个点的时候,重心平移的距离最多只有 1 1 1,而且是向着加入的点的方向平移,算一算当前大小就行了。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const
namespace IO{
inline char get_char(){
static cs int Rlen=1<<20|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
inline int getint(){
re char c;
while(!isdigit(c=gc()));re int num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
}
using namespace IO;
using std::cout;
using std::cerr;
cs int N=1e5+5;
int n;
std::vector<int> e[N];
inline void addedge(int u,int v){
e[u].push_back(v);
e[v].push_back(u);
}
int fa[N],dep[N],top[N],siz[N],son[N];
int in[N],out[N],dfs_clock;
void dfs1(int u){
siz[u]=1;
in[u]=++dfs_clock;
for(re int v:e[u])if(v!=fa[u]){
fa[v]=u;
dep[v]=dep[u]+1;
dfs1(v);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]])son[u]=v;
}
out[u]=dfs_clock;
}
void dfs2(int u){
if(son[u]){
top[son[u]]=top[u];
dfs2(son[u]);
}
else return ;
for(re int v:e[u])if(v!=fa[u]&&v!=son[u]){
top[v]=v;
dfs2(v);
}
}
inline int LCA(int u,int v){
while(top[u]^top[v])dep[top[u]]<dep[top[v]]?v=fa[top[v]]:u=fa[top[u]];
return dep[u]<dep[v]?u:v;
}
inline int jump(int u,int to){
int res;
while(top[u]^top[to])u=fa[res=top[u]];
return u==to?res:son[to];
}
int bit[N];
inline void add(int pos){
for(;pos;pos^=pos&-pos)++bit[pos];
}
inline int query(int pos){int res=0;
for(;pos<=n;pos+=pos&-pos)res+=bit[pos];
return res;
}
inline int query(int l,int r){
return query(l)-query(r+1);
}
int G=1;
ll ans;
signed main(){
std::ios::sync_with_stdio(false);cout.tie(NULL);
n=getint()+1;
for(int re i=2;i<=n;++i)addedge(i,getint());
dfs1(1),top[1]=1,dfs2(1);
add(in[1]);
for(int re i=2;i<=n;++i){
add(in[i]);
int lca=LCA(G,i);
ans+=dep[G]+dep[i]-dep[lca]*2;
if(lca==G){
int u=jump(i,G),siz=query(in[u],out[u]);
if(siz>i-siz)G=u,ans-=siz*2-i;
}
else {
int u=fa[G],siz=query(in[G],out[G]);
if(i-siz>siz)G=u,ans-=i-siz*2;
}
cout<<ans<<"\n";
}
return 0;
}