思路源于某巨佬:巨佬博客
对于 1<=k<=n 答案最大是n-1 最小是0 于是我们从大到小枚举答案(这样的话小的值会覆盖大的值) 然后看这个答案对应的最小k是多少 最后在做一遍前缀最小值就行了
找ans对应最小k的过程肯定是贪心的 我们找到深度最大的点 向上走ans步(找ans级祖先) 然后把它的子树都标记就可以了
当最大的深度<=nowans时 退出循环
当ans 确定的时候 最多进行 n/(ans+1) 次操作 每次操作复杂度是 logn级别的
所以 总复杂度 是 nlogn^2 (调和级数求和是logn级别的)
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+100;
vector<int>g[N],st;
int tot,in[N],out[N],n,ans[N],rnk[N];
int fa[N][22],dep[N];
typedef long long ll;
void init(){
tot=0;
for(int i = 1; i <= n; i++) g[i].clear(),ans[i]=n-1;
}
void dfs(int u,int f){
rnk[in[u]=++tot]=u;
dep[u]=dep[f]+1;
fa[u][0]=f;
for(int i = 1; i <= 19; i++) fa[u][i]=fa[fa[u][i-1]][i-1];
for(auto v:g[u]){
if(v==f) continue;
dfs(v,u);
}
out[u]=tot;
}
int kth(int u,int k){
int bit=0;
while(k){
if(k&1) u=fa[u][bit];
k>>=1;
bit++;
}
return u;
}
struct segTree{
int nd,mx;
bool cov;
}T[N<<2];
#define ls id<<1
#define rs ls|1
#define mid (l+r>>1)
#define lson ls,l,mid
#define rson rs,mid+1,r
void pushup(int id){
T[id].mx=0;
if(!T[ls].cov&&T[ls].mx>T[id].mx) T[id]=T[ls];
if(!T[rs].cov&&T[rs].mx>T[id].mx) T[id]=T[rs];
}
void update(int id,int l,int r,int L,int R,int v){
if(L<=l&&R>=r){
T[id].cov=v;
return;
}
if(L<=mid) update(lson,L,R,v);
if(R>mid) update(rson,L,R,v);
pushup(id);
}
void build(int id,int l,int r){
T[id].cov=false;
if(l==r){
T[id].mx=dep[T[id].nd=rnk[l]];
return;
}
build(lson);build(rson);
pushup(id);
}
int main(){
while(~scanf("%d",&n)){
init();
for(int i = 2; i <= n; i++){
int x;
scanf("%d",&x);
g[i].push_back(x);
g[x].push_back(i);
}
dep[0]=-1;dfs(1,0);
build(1,1,n);
for(int nowans=n-1; nowans >= 0; nowans--){
int ndcos=0;
st.clear();
while(1){
ndcos++;
if(T[1].mx<=nowans) break;
int u = T[1].nd;
u = kth(u,nowans);
update(1,1,n,in[u],out[u],1);
st.push_back(u);
}
ans[ndcos]=nowans;
for(auto v:st) update(1,1,n,in[v],out[v],0);
}
for(int i = 2; i <= n; i++) ans[i]=min(ans[i],ans[i-1]);
ll sum=0;
for(int i = 1; i <= n; i++) sum+=ans[i];
printf("%lld\n",sum);
}
return 0;
}