CF846E Chemistry in Berland 题解
题目链接:CF846E Chemistry in Berland
题意:
cxy 是 Berland 大学化学系的研究生。她需要进行一个复杂的实验来撰写她的论文,但是 Berland 的实验室并没有包含这个实验所需要的所有材料。
幸运的是,化学法则允许物质之间的转化(不过 Berland 的化学与我们的不同),但是物质转化的规则有点奇怪。
Berland 化学家知道 $n$ 种材料,按照某种顺序编号为 $1, 2, \cdots, n$。每种材料都可以与其它一种材料互相转化。正式地,对任意 $2 \leq i \leq n$,都存在两个正整数 $x_i, k_i$ 表示一种合理的转化:$k_i \, \mathrm{kg}$ 的材料 $x_i$ 可以转化成 $1 \, \mathrm{kg}$ 的材料 $i$,而 $1 \, \mathrm{kg}$ 的材料 $i$ 只能变成 $1 \, \mathrm{kg}$ 的材料 $x_i$。
由于 Berland 的化学系统比较奇怪,因此转化只允许整数质量的材料。
对每个 $1 \leq i \leq n$,cxy 知道她的实验中需要 $a_i \, \mathrm{kg}$ 的材料 $i$,且实验室中只有 $b_i \, \mathrm{kg}$。她想知道,是否存在一种转化方案,使得最终实验能够顺利进行?
输入格式:
第一行包含一个正整数 $n~(n \leq 10^5)$,表示 Berland 化学家发现的材料的数量。
第二行包含 $n$ 个正整数 $b_1, b_2, \cdots, b_n~(b_i \leq 10^{12})$,表示 Berland 实验室的供应。
第三行包含 $n$ 个正整数 $a_1, a_2, \cdots, a_n~(a_i \leq 10^{12})$,表示实验所需(各种物质的)的质量。
接下来的 $n-1$ 行,每行包含两个正整数,其中第 $i$ 行的整数 $x_{i+1}, k_{i+1}~(1 \leq x_{i+1} \leq i, k_{i+1} \leq 10^9)$ 表示物质 $i+1$ 可以与物质 $x_{i+1}$ 相互转化,且转化系数为 $k_{i+1}$ ( $k_i$ 的含义见题目描述)。
输出格式:
输出
YES
如果实验可以进行,否则输出NO
。
注意到这是一棵树,因此考虑树形dp。
设 $f_i$ 表示 $i$ 所在子树ok之后,材料 $i$ 还能剩多少(负的说明欠了)。显然叶子结点有 $f_i = b_i - a_i$ 。
考虑转移。对于 $u$ 的儿子 $v \in \mathtt{son}(u)$ ,我们考虑以下几种情况:
- $f_v \ge 0$ 。显然此时 $v$ 所在子树多出来的都可以给 $u$ 。
f[u] += f[v]
- $f_v < 0$ 。此时只能把 $u$ 的材料给 $v$ 。注意是
f[u] += k[v] * f[v]
。
最后只要看看是否有 $f_1>0$ 即可。然后你会发现这题其实是个贪心。
有趣的是这个算的过程中会爆 long long
。
解决办法,要么开 long double
,要么在小于 -INF
时直接让 f[u] = -INF
。
后者正确的原因在于 $f_i$ 增加的速度远低于其减少的速率,因此在 $f_i$ 极小时基本上是无解的。
时间复杂度 $\mathcal{O}(n)$
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(1e5+15))
int n,pos=1,f[N],a[N],k[N],b[N],limit[N],head[N];
struct Edge{ int u,v,next; } e[N];
void addEdge(int u,int v) { e[++pos]={u,v,head[u]}; head[u]=pos; }
void dfs(int u)
{
f[u] = b[u] - a[u];
for(int i=head[u]; i; i=e[i].next)
{
int v = e[i].v; dfs(v);
if(f[v] >= 0) f[u] += f[v];
else (f[v] <= -limit[v]) ? (f[u] = -INF) : (f[u] += f[v] * k[v]);
f[u] < -INF ? f[u] = -INF : 0;
}
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
cin >> n;
for(int i=1; i<=n; i++) cin >> b[i];
for(int i=1; i<=n; i++) cin >> a[i];
for(int i=2,fa; i<=n; i++)
{
cin >> fa >> k[i];
addEdge(fa,i); limit[i] = INF / k[i];
}
dfs(1);
cout << ((f[1] < 0) ? "NO\n" : "YES\n");
return 0;
}
参考文献: