Problem
辣鸡bzoj权限题,洛谷链接
题意概要:一棵 (n) 个点有根树。(Q) 次询问给出一个 (K),回答遍历完整棵树所需最少操作次数。每次操作可以选择访问不超过 (K) 个未访问的点,且这些点的父亲必须在之前被访问过。
Solution
一开始题意理解错了,以为同时段每个点都能扩展 (k) 次,然后就无耻地看了题解……但是有个证明网上都没写,这也正是写这篇博客的原因
大概意思是说,最优策略一定是先用 (i) 步取完前 (i) 层,然后选取剩下深度大于 (i) 的节点时每次都能取满。规范一下就是设 (s[i]) 表示深度大于 (i) 的节点个数,则答案为 (maxlimits_{i=1}^n {i+lceil frac {s[i]}k ceil})
然后就可以正常斜率优化了:维护点 ((i,s[i])) 的上凸包,每次询问 (i) 则在凸包上找斜率为 (-i) 的直线所切的点
但是看题解的时候不清楚前边那个式子的正确性,为什么会存在一条分界线使得上面可以用 (i) 次选完 (i) 层,下面可以随意选
思考了一会才弄懂,对于一棵树,从底层选择一个最深的深度 (d),显然满足下部节点可以随意选择(目前没有下部节点),但是有可能上部节点不能在层数次内选完。若出现这种情况,则上部一定存在若干层节点特别多,导致无法消完,可以考虑将分界线挪到某一层的上一层,由于这层节点特别多,可以保证下部节点可以随意选择。所以存在分界线比深度 (d) 浅,将 (d) 减小继续判断直到无法挪动,则找到分界线
至于为什么 (maxlimits_{i=1}^n {i+lceil frac {s[i]}k ceil}) 中取最大值即可,是因为如若没有选择到分界线,则算出的值一定比最终答案要小
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
template <typename _tp> inline void read(_tp&x){
char c11=getchar();x=0;while(!isdigit(c11))c11=getchar();
while(isdigit(c11))x=x*10+c11-'0',c11=getchar();
}
const int N = 1001000;
struct Edge{int v,nxt;} a[N+N];
int head[N], s[N], Ans[N], qy[N];
ll f[N];
int n,Q,tp,_,mxd;
void dfs(int x,int ds){
++s[ds], mxd = max(mxd, ds);
for(int i=head[x];i;i=a[i].nxt)
dfs(a[i].v,ds+1);
}
struct vec{
ll x,y;
inline void in(){read(x), read(y);}
friend inline vec operator - (const vec&A,const vec&B) {return (vec){A.x - B.x, A.y - B.y};}
friend inline db operator * (const vec&A,const vec&B) {return (db)A.x * B.y - (db)A.y * B.x;}
friend inline bool operator < (const vec&A,const vec&B) {return A.x!=B.x ? A.x<B.x : A.y>B.y;}
}p[N],stk[N];
int main(){
read(n), read(Q);
for(int i=1;i<=Q;++i) read(qy[i]);
for(int i=2,x;i<=n;++i) read(x), a[++_].v = i, a[_].nxt = head[x], head[x] = _;
dfs(1,0);
for(int i=n;~i;--i) s[i] += s[i+1];
for(int i=0;;++i){
vec nw = (vec) {i, s[i]};
while(tp > 1 and (stk[tp] - stk[tp-1]) * (nw - stk[tp]) >= 0) --tp;
stk[++tp] = nw;
if(!s[i]) break;
}
for(int i=1,t=1;i<=n;++i){
vec dir = (vec) {1, -i};
while(t < tp and (stk[t+1] - stk[t]) * dir < 0) ++t;
Ans[i] = (stk[t].y + (ll)i * stk[t].x + i-1) / i;
}
++mxd;
for(int i=1;i<=Q;++i){
if(Ans[qy[i]]) printf("%d ",Ans[qy[i]]);
else printf("%d ",mxd);
}
return 0;
}