luoguP5521 [yLOI2019] 梅深不见冬

luoguP5521 [yLOI2019] 梅深不见冬

Description

给定一棵 n个节点的树,在树上行走,每次要么选择一个没有到达过的子节点,要么返回父节点。想要在一个节点 u 放上梅花当且仅当 u 的任意子节点 v 都被放上了 w​ 朵梅花。在任意时刻可以收回任意节点的梅花。对于每个节点,求如果想在这个节点放梅花,则至少需要准备多少梅花。

Solution

容易注意到叶子节点的答案一定是本身的权值

而非叶节点一定是由下面的点限制的

因此考虑自底向上处理

显然对于一个节点i,他的答案最优是自身权值和所有儿子权值之和

不能达到最优在于有可能儿子所需的答案更大

于是考虑保证每个儿子都能填数,显然儿子所需的答案已经处理出来了

显然当你填了一个数后他的前置节点就不需要再放数了,也就是说这一部分可以在儿子间重复利用

很显然前置最多的那个点的前置的梅花一定可以反复放在其他儿子上

所以说答案一定不会大于这个点本身的值加上儿子的值再加上最多的前置

但是这一部分前置有一部分可以回收放在节点的权值上

所以考虑按前置排序,每次保证当前前置的时候尽可能多的放在节点上

答案显然更优

#include<bits/stdc++.h>

using namespace std;

inline int read()
{
    int f = 1,x = 0;
    char ch;
    do
    {
        ch = getchar();
        if(ch == '-') f = -1;
    }while(ch < '0'|| ch > '9');
    do
    {
        x = (x<<3) + (x<<1) + ch - '0';
        ch = getchar();
    }while(ch >= '0'&&ch <= '9');
    return f*x;
}

const int MAXN = 100000 + 10;

int n;
int ans[MAXN],f[MAXN],g[MAXN];
int w[MAXN];
vector<int>G[MAXN];
vector<pair<int,int> >son[MAXN];

inline void dfs(int x)
{
    ans[x] = w[x];
    if(!G[x].size()) return;
    for(int i=0;i<G[x].size();i++)
    {
        int v = G[x][i];
        dfs(v);
        son[x].push_back(make_pair(ans[v] - w[v],v));
    }
    sort(son[x].begin(),son[x].end());
    int res = 0;
    int sum = 0;
    for(int i=son[x].size()-1;i>=0;i--)
    {
        int v = son[x][i].second;
        if(res < ans[v])
        {
            sum += (ans[v] - res);
            res = ans[v] - w[v];
        }
        else res -= w[v];
    }
    if(res < w[x]) sum += (w[x] - res);
    ans[x] = sum;
//    cout << x << " " << f[x] << " " << ans[x] << " " << maxv << " " << w[maxi] << endl;
    return;
}

int main()
{
    n = read();
    for(int i=2;i<=n;i++)
    {
        G[read()].push_back(i);
    }
    for(int i=1;i<=n;i++) w[i] = read();
    dfs(1);
    for(int i=1;i<=n;i++) printf("%d ",ans[i]);
}
原文地址:https://www.cnblogs.com/wlzs1432/p/13783002.html