AtCoder

Luogu

分析

首先注意到黑色还是白色并不重要,因为黑白可以互换。因此我们染一棵子树的根节点时可以强制让它为黑色。

那么,当染一棵子树的根节点 $u$ 时,子树中黑点的权值和应为 $X_u$ ,白点的权值和应该尽量小。

这里白点权值和尽量小是贪心的思想,因为权值尽量小是一定能通过一些更改满足条件。

于是可以考虑 DP。设 $f_u$ 表示以 $u$ 为根的子树中白点权值和最小能为多少。

考虑转移。我们要求的实际上是在黑点权值和不超过 $X_u$ (小的可以通过根节点加回来)的情况下白点权值和最小是多少,于是做一个类似于背包的 DP 即可。

代码

// ===================================
//   author: M_sea
//   website: http://m-sea-blog.com/
// ===================================
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define re register
using namespace std;

inline int read() {
    int X=0,w=1; char c=getchar();
    while (c<'0'||c>'9') { if (c=='-') w=-1; c=getchar(); }
    while (c>='0'&&c<='9') X=X*10+c-'0',c=getchar();
    return X*w;
}

const int N=1000+10,V=5000+10;
const int inf=0x3f3f3f3f;

int n,X[N];

struct edge { int v,nxt; } e[N];
int head[N];
inline void addEdge(int u,int v) {
    static int cnt=0;
    e[++cnt]=(edge){v,head[u]},head[u]=cnt;
}

int f[N],dp[V],tmp[V];

inline void dfs(int u) { f[u]=inf;
    for (re int i=head[u];i;i=e[i].nxt) dfs(e[i].v);
    memset(dp,inf,sizeof(dp)); dp[0]=0;
    for (re int i=head[u];i;i=e[i].nxt) { int v=e[i].v;
        for (re int j=0;j<=X[u];++j) tmp[j]=inf;
        for (re int j=0;j<=X[u];++j) {
            if (j>=X[v]) tmp[j]=min(tmp[j],dp[j-X[v]]+f[v]);
            if (j>=f[v]) tmp[j]=min(tmp[j],dp[j-f[v]]+X[v]);
        }
        for (re int j=0;j<=X[u];++j) dp[j]=tmp[j];
    }
    for (re int i=X[u];~i;--i) f[u]=min(f[u],dp[i]);
}

int main() {
    n=read();
    for (re int i=2;i<=n;++i) addEdge(read(),i);
    for (re int i=1;i<=n;++i) X[i]=read();
    dfs(1); puts(f[1]<inf?"POSSIBLE":"IMPOSSIBLE");
    return 0;
}
最后修改:2021 年 03 月 23 日 10 : 15 PM