【题目】
CF
给定一棵
n
n
n个节点的有根树,定义
f
(
x
,
y
)
f(x,y)
f(x,y)(其中
y
y
y是
x
x
x的祖先)表示
y
y
y后代中除
x
x
x以外深度不大于
x
x
x的节点数。定义
g
(
x
)
=
∑
f
(
x
,
y
)
g(x)=\sum f(x,y)
g(x)=∑f(x,y)。求所有
g
(
x
)
g(x)
g(x)。
n
≤
5
×
1
0
5
n\leq 5\times 10^5
n≤5×105
【解题思路】
一个比较
naive
\text{naive}
naive的思路就是将节点按深度排序后一层一层做,用树链剖分解决这个东西可以做到
O
(
n
log
2
n
)
O(n\log^2 n)
O(nlog2n),但是需要一定卡常技巧。我们可以考虑优化这个过程。
我们要求的东西可以表示成这个:
g
(
x
)
=
∑
d
e
p
(
y
)
≤
d
e
p
(
x
)
d
e
p
(
l
c
a
(
x
,
y
)
)
−
d
e
p
(
x
)
g(x)=\sum_{dep(y)\leq dep(x)} dep(lca(x,y))-dep(x)
g(x)=dep(y)≤dep(x)∑dep(lca(x,y))−dep(x)
等价于:
g
(
f
a
(
x
)
)
+
∑
d
e
p
(
y
)
=
d
e
p
(
x
)
d
e
p
(
l
c
a
(
x
,
y
)
)
−
d
e
p
)
(
x
)
g(fa(x))+\sum_{dep(y)=dep(x)}dep(lca(x,y))-dep)(x)
g(fa(x))+dep(y)=dep(x)∑dep(lca(x,y))−dep)(x)
观察到,如果我们先将这些节点按
d
f
s
dfs
dfs序排序,对于第
i
i
i个节点,它前面的节点与它的
l
c
a
lca
lca的深度是单调不降的。
那么我们可以维护一个单调栈,若相邻节点与当前节点的 l c a lca lca相同可以将相邻节点合并,因为显然之后它们的 l c a lca lca也是相同的,同样也不存在栈中间会合并的情况。这样每个深度下总的合并次数就是 O ( 点 数 ) O(点数) O(点数)的了。
总复杂度就是
O
(
n
log
n
)
O(n\log n)
O(nlogn),
log
\log
log来自于求
l
c
a
lca
lca
因为求
l
c
a
lca
lca的次数比较多,如果使用
O
(
n
log
n
)
O(n\log n)
O(nlogn)预处理,
O
(
1
)
O(1)
O(1)询问的欧拉序
l
c
a
lca
lca应该会更优秀。不过观察到空间比较紧就没搞了。
【参考代码】
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
const int N=5e5+10;
int n,rt,fc[20],Log[N];
ll ans[N];
namespace IO
{
int read()
{
int ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
void write(ll x){if(x>9)write(x/10);putchar(x%10^48);}
void writesp(ll x){write(x);putchar(' ');}
}
using namespace IO;
namespace Tree
{
int tot,ind;
int head[N],dep[N],pos[N],fa[20][N];
vector<int>st[N];
struct Tway{int v,nex;}e[N<<1];
void add(int u,int v)
{
e[++tot]=(Tway){v,head[u]};head[u]=tot;
e[++tot]=(Tway){u,head[v]};head[v]=tot;
}
void dfs(int x)
{
pos[x]=++ind;
for(int i=1;fc[i]<=dep[x];++i) fa[i][x]=fa[i-1][fa[i-1][x]];
for(int i=head[x];i;i=e[i].nex)
{
int v=e[i].v;
if(v==fa[0][x]) continue;
dep[v]=dep[x]+1;fa[0][v]=x;st[dep[v]].pb(v);dfs(v);
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
for(int i=0,t=dep[x]-dep[y];i<20;++i)
if(t&fc[i]) x=fa[i][x];
for(int i=19;~i;--i) if(fa[i][x]^fa[i][y])
x=fa[i][x],y=fa[i][y];
return x==y?x:fa[0][x];
}
}
using namespace Tree;
namespace DreamLolita
{
struct Stack
{
int len,a[N],b[N],c[N];//a=id,b=dep,c=pos
void clear(){len=0;}
void push(int x,int y,int z){++len;a[len]=x;b[len]=y;c[len]=z;}
void pop(){--len;}
int topa(){return a[len];}
int topb(){return b[len];}
ll calctop(){return (ll)b[len]*(c[len]-c[len-1]);}
}S;
void initsomething()
{
fc[0]=1;for(int i=1;i<20;++i)fc[i]=fc[i-1]<<1;
for(int i=2;i<N;++i)Log[i]=Log[i>>1]+1;
}
bool cmp(int x,int y){return pos[x]<pos[y];}
void calc(const vector<int> &a)
{
ll res=0;S.clear();
for(int i=0;i<(int)a.size();++i)
{
int x=a[i];
if(!i) S.push(x,0,0);
else
{
for(;;)
{
int y=lca(S.topa(),x);
if(S.topb()<=dep[y]){S.push(x,dep[y]+1,i);break;}
res-=S.calctop();S.pop();
}
res+=S.calctop();
}
ans[x]+=res;
}
}
void solve()
{
n=read();
for(int i=1,x;i<=n;++i)
{
x=read();
if(x) add(i,x); else rt=i;
}
initsomething();dfs(rt);
for(int d=1;d<=n;++d)
{
sort(st[d].begin(),st[d].end(),cmp);
for(int i=0;i<(int)st[d].size();++i) ans[st[d][i]]+=ans[fa[0][st[d][i]]]+d;
calc(st[d]);reverse(st[d].begin(),st[d].end());calc(st[d]);
}
for(int i=1;i<=n;++i) writesp(ans[i]);
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("CF860E.in","r",stdin);
freopen("CF860E.out","w",stdout);
#endif
DreamLolita::solve();
return 0;
}