先考虑暴力如何处理:
已知标记点的个数最小化 最大AD距离不好求。
但反过来,已知最大AD距离x,求最少的标记点个数很好求:直接每次删除深度最大的点的x倍祖宗所在的子树直到全部删完。(每次至少删除x+1个节点,也就是说点数最多只需要n/(x+1)+1)
那么我们可以枚举最大AD距离,求出每个AD距离x下最少需要的标记点数y,那么 K >=y 时,最大AD距离一定小于等于y。
求出所有的然后dp滚一下即可。
下面考虑已知x如何 快速求出最少需要的标记点数:
由于每次删除,显然容易想到:用dfs序建立线段树维护每个dfs序对应节点的深度。
每次查找到深度最大的节点的x倍祖宗,然后删除其所在子树。(记录操作节点,方便后面初始化)
直到所有节点被删除,则找到了最少标记点数nm。
下面考虑时间复杂度:
每个距离x会找 n/x 次左右的点删除子树。
总共找: n+n/2+n/2+…………+n/n =nln(n)次
每次需要log的复杂度在线段树上面,所以总体复杂度为:
n ln(n) * logn
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define re register
#define ls (o<<1)
#define rs (o<<1|1)
#define m (l+r)/2
#define pb push_back
const double PI= acos(-1.0);
const int M = 2e5+7;
int head[M],cnt=1;
void init(int n){cnt=1;for(int i=0;i<=n;i++)head[i]=0;}
struct EDGE{int to,nxt,w;}ee[M*2];
void add(int x,int y,int w){ee[++cnt].nxt=head[x],ee[cnt].w=w,ee[cnt].to=y,head[x]=cnt;}
int st[M],ed[M],sz,dep[M],dfn[M];
int f[M][21];
void dfs(int x,int fa){
// cout<<" ============ "<<x<<" "<<fa<<" "<<sz+1<<endl;
st[x]=++sz;
dfn[sz]=x;
f[x][0]=fa;
for(int i=head[x];i;i=ee[i].nxt){
int y=ee[i].to;
if(y==fa)continue;
dep[y]=dep[x]+1;
dfs(y,x);
}
ed[x]=sz;
}
int gt(int x,int len){//x的len辈祖宗
for(int i=20;i>=0;i--){
if(len>=(1<<i))x=f[x][i],len-=(1<<i);
}
return x;
}
vector<int>cg;//改变的节点
int tr[M<<2],ps[M<<2],tg[M<<2],tr0[M<<2],ps0[M<<2];
void pu(int o){
if(tr[ls]>=tr[rs])tr[o]=tr[ls],ps[o]=ps[ls];
else tr[o]=tr[rs],ps[o]=ps[rs];
}
void pd(int o){
if(tg[o]==0)return ;
tr[ls]=tr[rs]=0;
tg[ls]=tg[rs]=1;
tg[o]=0;
}
void bd(int o,int l,int r){
tg[o]=0;
if(l==r){
tr[o]=tr0[o]=dep[dfn[l]];
ps[o]=ps0[o]=l;
return ;
}
bd(ls,l,m);
bd(rs,m+1,r);
pu(o);
tr0[o]=tr[o];
ps0[o]=ps[o];
}
void up(int o,int l,int r,int x,int y){//区间赋值为0
cg.pb(o);
if(x<=l&&r<=y){
tr[o]=0;tg[o]=1;
return ;
}
pd(o);
if(x<=m)up(ls,l,m,x,y);
if(y>m)up(rs,m+1,r,x,y);
pu(o);
}
int ans[M];
int main()
{
int n;
while(~scanf("%d",&n)){
for(int i=0;i<=n;i++)head[i]=0,ans[i]=n+1;
cnt=1;sz=0;
int v;
for(int i=2;i<=n;i++)scanf("%d",&v),add(i,v,1),add(v,i,1);
dep[1]=1;dfs(1,0);
for(int l=1;l<=20;l++)
for(int i=1;i<=n;i++)
f[i][l]=f[f[i][l-1]][l-1];
bd(1,1,n);
// for(int i=1;i<=n;i++)cout<<" == "<<i<<" "<<st[i]<<" "<<ed[i]<<endl;
for(int x=1;x<n;x++)//枚举最大AD距离,求出最少需要多少个key,使得所有点的AD距离都小于等于x
{
int nm=0;
cg.clear();
while(1){//每个关键点会删去至少x+1个节点。最多执行n/x次,的单次均摊复杂度为log*调和级数
int mx=tr[1],id=dfn[ps[1]];
if(mx==0)break;
int fd=gt(id,x);
nm++;
// cout<<fd<<" ****************- "<<mx<<" "<<id<<" = "<<endl;
if(fd<=1)break;
up(1,1,n,st[fd],ed[fd]);
// cout<<" "<<st[fd]<<" "<<ed[fd]<<endl;
}
//cout<<x<<" ------------------- "<<nm<<endl;
for(auto x:cg)tr[x]=tr0[x],ps[x]=ps0[x],tg[x]=0;//初始化线段树
ans[nm]=min(ans[nm],x);
}
ll pr=0;
for(int i=2;i<n;i++) ans[i]=min(ans[i],ans[i-1]);
for(int i=1;i<n;i++)pr+=ans[i];
//cout<<i<<" - "<<ans[i]<<endl;;
printf("%lld\n",pr);
}
return 0;
}
/*
7
1 1 2 2 3 3
9
1 1 2 2 3 3 4 7
10
1 2 3 2 1 3 5 3 2
*/