P3571 题解
实现上与已有所有题解不同。这份代码是可以通过测试的。分析部分有参考出题人(也许是吧)的题解。
首先我们发现,正着搞会有很多限制,所以考虑倒着搞,每次去掉一些叶子,至多
令树高为
那么可不可以取等呢?我们设计贪心策略如下:每次去掉深度最大的
然后进行分类讨论:
-
假设当前树高为
H ,如果当前树的宽度不大于k ,很明显只需要H 次删光,并且因为每次至多删光一层,所以也只能至少H 次删光,此时ans 必然取等。 -
如果当前树的宽度不大于
k ,那么我们可以选择它最深的k 个叶子,并将它们删除,它们的深度分别为d_1\ge d_2\ge...\ge d_k 。同时,最深的一层,即深度为H 的一层必定删光,所以新树高H'=H-1 。-
对于所有
h<d_k ,在删除这k 个叶子后,它们对应的h+\left\lceil\dfrac{s_{h+1}}{k}\right\rceil 刚好减少1 。 -
对于所有
h\ge d_k ,我们可以发现h>d_k 的所有层点数不超过下一层的点数加上这一层的叶子数,都不超过k 。所以第h+1 层的点数< k ,对于所有h\ge d_k ,有h+\left\lceil\dfrac{s_{h+1}}{k}\right\rceil\le h+1+\left\lceil\dfrac{s_{h+2}}{k}\right\rceil ,而最后一层,即深度为H'=H-1 的那一层的值为H-1 ,所以对于所有h\ge d_k ,h+\left\lceil\dfrac{s_{h+1}}{k}\right\rceil 都小于H-1 。
-
综上,在这种贪心策略下,每一次操作都可以使
所以我们证明了
容易知道对于固定的
于是题目变为给出
亲测不开 O2 优化时,最慢的一个点 308ms。
ll n,qwq,mxq=0,mxd=0,l=1,r=0,a[1000010],f[1000010],t[4000010],d[1000010],s[1000010];
inline ll cal(ll x,ll v){return s[x+1]+x*v;}
void upd(ll x,ll l,ll r,ll u){
ll &v=t[x],mid=(l+r)>>1;
if(cal(u,mid)>cal(v,mid))swap(u,v);
if(cal(u,l)>cal(v,l))upd(x<<1,l,mid,u);
if(cal(u,r)>cal(v,r))upd(x<<1|1,mid+1,r,u);
}
ll query(ll x,ll l,ll r,ll v){
if(l==r)return cal(t[x],v);
ll mid=(l+r)>>1;
if(v<=mid)return max(cal(t[x],v),query(x<<1,l,mid,v));
else return max(cal(t[x],v),query(x<<1|1,mid+1,r,v));
}
int main(){
n=read();qwq=read();d[1]=s[1]=1;
for(ll i=1;i<=qwq;i++)mxq=max(mxq,a[i]=read());
for(ll i=2;i<=n;i++){d[i]=d[read()]+1;s[d[i]]++;mxd=max(mxd,d[i]);}
for(ll i=mxd-1;i;i--){s[i]+=s[i+1];upd(1,1,n,i);}
for(ll i=1;i<=mxq;i++)f[i]=(query(1,1,n,i)-1)/i+1;
for(ll i=1;i<=qwq;i++){write(f[a[i]]);putchar(' ');}
return 0;
}
AC 记录