题解 CF1059E Split the Tree
题意
将一棵 $n$ 个节点的以 1 为根的树拆成若干个链,每条链满足:
- 点数不超过 $L$。
- 点权和不超过 $S$。
- 对于链上的某一位置 $i$($i\ge 2$),满足 $v_i$ 是 $v_{i-1}$ 的父亲。
求最少分成几个链。特别的,倘若无解,输出 -1。
解决
显然的,我们是从儿子转移到父亲,问题就在于我们父亲节点的选择方式,是再起炉灶,还是承接某一个儿子,又承接哪一个儿子。
想想看,倘若我们将这个树当做一条链,我们应当如何解决。
明显的,我们应该是在能延伸的情况下就尽量延伸,这样我们可以让我们这个新生的节点的末尾更远。
也就是说,当我们在树上时,我们只要有一个儿子能有余地转移,我们就应当承接转移。
接下来的问题在于承接哪一个儿子。
贪心的,我们选择可以延伸更远的那一个儿子的链。
对于处理延伸的长度,我们可以用树上倍增处理,维护 $fa$ 数组与 $sum$ 数组,在处理每个点时,先将自己加上,再从大到小枚举二进制位,倘若满足,立即转移。
总结一下,我们在解决这道题时,我们用倍增维护我们的延伸最远,再在儿子中寻找最远,倘若没有,就从自己再起炉灶。
特殊的,为了判断 -1,我们需要在一开始就判断是否存在一个节点的权值大于 $S$,倘若存在,那我们的答案就是 -1。
核心代码。
inline void redfs(int now,int fath) {
int u=now,tot=val[now],cnt=0;
for(int i=19; ~i; --i) {
if(cnt+(1<<i)+1>L) continue;
if(tot+sum[u][i]>S) continue;
cnt+=(1<<i);
tot+=sum[u][i];
}
mx[now]=cnt;
for(auto to:lj[now]) {
if(to==fath) continue;
redfs(to,now);
sy[now]=max(sy[now],sy[to]-1);
}
if(sy[now]==-1) {
++ans;
sy[now]=mx[now];
}
}

浙公网安备 33010602011771号