【題解】 [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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章