【题解】 [yLOI2019] 梅深不见冬

[yLOI2019] 梅深不见冬

\(\text{Solution:}\)

题意比较清晰就不赘述了。

先从一个 naive 的想法开始:如果要满足题目所说条件,那么一定就是其所有孩子都要先被占满梅花,再占自己。

那么一个 dfs 的轮廓就有了。这题一定是一个自底向上更新的解决流程。

那么我们试着给出一个解决方案:设 \(ans[x]\) 表示在 \(x\) 点符合要求所需要的最小梅花数量。怎么更新?

我们发现,题目说可以随时回收梅花,也就意味着对应地,把一个点填满后,必然会回收一些梅花。我们记 \(rest[x]\) 表示填好这个点所回收掉的梅花数。

那么,考虑一个个儿子依次更新:第一个儿子回收上来的点只能作用于后面到达的儿子上。所以我们需要 确定某个顺序 来使得 \(rest[]\) 数组的利用率最大。

考虑按照 \(rest[x]\) 从大到小排序。这样,每次放完一个孩子后用之前的 \(rest\) 更新,就是最优的情况。

\(Proof:\)

首先如果一个点的 \(ans[x]>ans[x']\) 并且 \(rest[x]<rest[x']\) 显然不优。

那么,假定存在 \(x,x'\) 满足 \(ans[x]<ans[x'],rest[x]<rest[x']\)

选择前者的答案会是 \(ans[now]+ans[x],rest[now]+rest[x]\)

选择后者的答案会是 \(ans[now]+ans[x'],rest[now]+rest[x']\)

暂且假定两者会被放到第一个位置,后面的情况一致。这种情况下:

若后面的所有消耗不超过 \(rest[x],\) 那么两者在此时一致。

若后面的所有消耗超过了 \(rest[x],\) 那么后者更优。

那么当两者不位于序列第一位时,设当前 \(rest[now]=r.\)

那么,如果 \(r\leq ans[x]\) 就和上述情况无异了。

\(r\ge ans[x']\) 显然是后者更优。

\(r\) 居于两者之间的时候,为了选择后者需要花费 \(cost=ans[x']-r\) 的代价。

那么此时考虑后面的情况,我们来考虑 \(rest\)使用率

这是因为:

所有的 \(ans[v][v\in son[now]]\) 都会被累加上去,唯一可以使得代价变小的就是 \(rest\) 的利用率。

观察到,后者所面对的情况的 \(cost'\) 应当小于等于前者所面对的 \(cost'.\)

而由此,后者在前面利用率比前者只高不低的情况下更容易将后面的所有值覆盖,也就是其利用率必然比前者高。

综上可以得出结论,按照 \(rest[x]\) 排序即可。

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,m;
int w[N];
int pa[N],ans[N];
int rest[N];
vector<int>G[N];
inline void link(int x,int y){
	G[x].push_back(y);
}
inline bool cmp(int x,int y){
	return rest[x]>rest[y];
}
void dfs(int x){
	for(auto v:G[x])dfs(v);
	sort(G[x].begin(),G[x].end(),cmp);
	ans[x]=0,rest[x]=0;
	if(G[x].empty()){
		ans[x]=w[x];
		rest[x]=0;
		return;
	}
	ans[x]+=ans[G[x][0]];
	rest[x]+=rest[G[x][0]];
	for(int i=1;i<(int)G[x].size();++i){
		if(rest[x]>=ans[G[x][i]]){
			rest[x]-=ans[G[x][i]];
			rest[x]+=rest[G[x][i]];
		}
		else{
			ans[x]+=ans[G[x][i]];
			ans[x]-=rest[x];
			rest[x]=rest[G[x][i]];
		}
	}
	if(rest[x]>=w[x])rest[x]-=w[x];
	else {
		ans[x]+=w[x]-rest[x];
		rest[x]=0;
	}
	for(auto v:G[x])rest[x]+=w[v];
}
int main(){
	freopen("in.txt","r",stdin);
	scanf("%d",&n);
	for(int i=1;i<n;++i)scanf("%d",&pa[i]),link(pa[i],i+1);
	for(int i=1;i<=n;++i)scanf("%d",&w[i]);
	dfs(1);
	for(int i=1;i<=n;++i)printf("%d ",ans[i]);
	return 0;
}
posted @ 2021-09-29 15:01  Refined_heart  阅读(33)  评论(0编辑  收藏  举报