luogu#P11051. [IOI2024] 树上代价
[IOI2024] 树上代价
题目背景
提交时请不要引用 tree.h
。
请不要使用 C++14 (GCC 9) 提交。
题目描述
有一棵包括 个结点的树,结点从 到 编号。结点 是树的根。除根以外的每个结点都有唯一的父结点。对所有满足 的 ,结点 的父结点为 ,这里有 。我们约定 。
对所有结点 (), 的子树是如下结点组成的集合:
- ,以及
- 所有父结点为 的结点,以及
- 所有父结点的父结点为 的结点,以及
- 所有父结点的父结点的父结点为 的结点,以及
- 以此类推。
下图给出了一个包含 个结点的树的例子。每个箭头都从某个结点连向它的父结点(根结点除外,因为它没有父结点)。结点 的子树包括结点 和 。结点 的子树包括树中的全部 个结点,而结点 的子树仅包括结点 自己。
每个结点都被赋以非负整数的权重。我们将结点 ()的权重记为 。
你的任务是写一个程序来回答 个询问,其中每个询问都用一对正整数 来表示。对于询问的回答,应按照如下要求进行计算。
对树中的每个结点,都指派一个整数,称为系数。这样的指派结果被描述成一个序列 ,这里 ()是指派给结点 的系数。我们称该序列为一个系数序列。注意,系数序列中的元素可以取负值、 或正值。
对某个询问 ,一个系数序列被称为是有效的,如果对于每个结点 ()都有如下条件成立:结点 的子树中的系数之和不小于 且不大于 。
对于一个给定的系数序列 ,结点 的代价为 ,这里 表示 的绝对值。最后,总体代价为所有结点的代价之和。你的任务是,对于每个询问,计算出可以由某个有效系数序列达到的最小总体代价。
可以证明,对于任意询问,都至少存在一个有效的系数序列。
实现细节
你需要实现如下两个函数:
void init(std::vector<int> P, std::vector<int> W)
- ,:两个长度为 的整数数组,记录了结点的父结点和权重。
- 对于每个测试样例,在评测程序与你的程序开始交互时,该函数将被恰好调用一次。
long long query(int L, int R)
- ,:两个整数,描述一次询问。
- 对于每个测试样例,在
init
被调用后,该函数将被调用 次。 - 该函数应该返回对给定询问的答案。
输入格式
评测程序示例读取如下格式的输入:
N
P[1] P[2] ... P[N-1]
W[0] W[1] ... W[N-2] W[N-1]
Q
L[0] R[0]
L[1] R[1]
...
L[Q-1] R[Q-1]
这里的 和 (),是对 query
的第 次调用的输入参数。注意,输入数据中的第二行中仅包括 个整数,因为评测程序示例并不读取 的值。
输出格式
评测程序示例按照如下格式打印你的答案:
A[0]
A[1]
...
A[Q-1]
这里的 (),是第 次调用 query
时返回的值。
3
0 0
1 1 1
2
1 1
1 2
3
2
提示
考虑如下调用:
init([-1, 0, 0], [1, 1, 1])
这棵树包含 个结点:根结点以及它的 个子结点。所有结点的权重均为 。
query(1, 1)
本次询问有 ,这意味着每个子树中的系数之和都必须等于 。考虑系数序列 。这棵树以及相应的系数(在阴影矩形中)图示如下。
对每个结点 (), 的子树中全部结点的系数之和均为 。因此,系数序列是有效的。总体代价的计算如下:
结点 | 权重 | 系数 | 代价 |
---|---|---|---|
因此总体代价为 。这是唯一的有效系数序列,因此调用应该返回 。
query(1, 2)
对于该询问的最小总体代价为 ,可以在系数序列为 时达到。
约束条件
- 对所有满足 的 ,都有
- 对所有满足 的 ,都有
- 在每次询问中,都有
子任务 | 分数 | 额外的约束条件 |
---|---|---|
1 | ;对所有满足 的 ,都有 | |
2 | ; | |
3 | ; | |
4 | 对所有满足 的 ,都有 | |
5 | 对所有满足 的 ,都有 | |
6 | ||
7 | 没有额外的约束条件。 |