yaoxi-std 的博客

$\text{开}\mathop{\text{卷}}\limits^{ju\check{a}n}\text{有益}$

0%

P8274 [USACO22OPEN] Balancing a Tree G

P8274 [USACO22OPEN] Balancing a Tree G

题面

题目链接

解法

可以发现确定了 $s_1$ 的值以后就知道了其他的所有 $s$,构造方法如下:

让所有 $s_i$ 都尽量靠近 $s_1$ 显然使得 $Ans$ 最小。

而此时的 $Ans$ 也很容易一遍 $dfs$ 进行 $O(n)$ 的计算,但是复杂度 $n \times (r_1 - l_1)$。

思考 $s_1$ 对答案的贡献,显然就是 $\max(0, \max(l_i) - s_1, s_1 - \min(r_i))$,可以预处理出 $l_i$ 和 $r_i$ 的最大值 $O(1)$ 计算。

但是这不是最终的答案,有可能有 $v$ 的祖先 $u$ 使得 $r_u < s_1 < l_v$(反之同理,即 $r_v < s_1 < l_u$)。而此时,由于要让所有 $s_i$ 靠近 $s_1$,则 $s_u$ 的取值一定为 $r_u$,$s_v$ 的取值一定为 $l_v$,对答案的贡献就成了 $l_v - r_u$。

而如果 $s_u$ 和 $s_v$ 在 $s_1$ 的同侧,那么 $|s_u - s_v|$ 的贡献就一定小于 $s_1$ 对答案的贡献,这部分可以不用考虑。于是得出重要结论:非根节点和其非根节点的祖先对答案的贡献是固定的

因此,不难想到可以一遍 $dfs$ 就处理出 $max(l_v - r_u)$,在枚举 $s_1$ 的时候可以 $O(1)$ 加入贡献。于是复杂度变为 $O(r_1 - l_1)$。虽然还是无法通过,但是有了不少的优化空间。

由于一部分答案的贡献是固定的,所以我们只考虑另一部分 $\max(0, \max(l_i) - s_1, s_1 - \min(r_i))$。那么显然 $s_1 = \frac{\max(l_i) - \min(r_i)}{2}$ 是最优的。

于是这题就结束了,时间复杂度 $O(n)$,为一遍 $dfs$ 的复杂度。

AC代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/**
* @file: T3.cpp
* @author: yaoxi-std
* @url:
*/
// #pragma GCC optimize ("O2")
// #pragma GCC optimize ("Ofast", "inline", "-ffast-math")
// #pragma GCC target ("avx,sse2,sse3,sse4,mmx")
#include <bits/stdc++.h>
using namespace std;
#define resetIO(x) \
freopen(#x ".in", "r", stdin), freopen(#x ".out", "w", stdout)
#define debug(fmt, ...) \
fprintf(stderr, "[%s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
template <class _Tp>
inline _Tp& read(_Tp& x) {
bool sign = false; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) sign |= (ch == '-');
for (x = 0; isdigit(ch); ch = getchar()) x = x * 10 + (ch ^ 48);
return sign ? (x = -x) : x;
}
template <class _Tp>
inline void write(_Tp x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) write(x / 10);
putchar((x % 10) ^ 48);
}
bool m_be;
using ll = long long;
const int MAXN = 1e5 + 10;
const int INF = 0x3f3f3f3f;
int n, m, tp, fa[MAXN], lp[MAXN], rp[MAXN], ans[MAXN];
vector<int> g[MAXN];
inline void chkmin(int& x, int y) { (x > y) && (x = y); }
inline void chkmax(int& x, int y) { (x < y) && (x = y); }
int dfs(int u, int mxl, int mnr) {
int ret = max({0, lp[u] - mnr, mxl - rp[u]});
chkmax(mxl, lp[u]), chkmin(mnr, rp[u]);
for (auto v : g[u]) chkmax(ret, dfs(v, mxl, mnr));
return ret;
}
bool m_ed;
signed main() {
// debug("Mem %.5lfMB.", fabs(&m_ed - &m_be) / 1048576);
int cas; read(cas), read(tp);
while (cas--) {
read(n), m = 0;
for (int i = 1; i <= n; ++i) g[i].clear();
for (int i = 2; i <= n; ++i) read(fa[i]), g[fa[i]].push_back(i);
for (int i = 1; i <= n; ++i) read(lp[i]), read(rp[i]);
int mxl = 0, mnr = INF;
for (int i = 1; i <= n; ++i)
chkmax(mxl, lp[i]), chkmin(mnr, rp[i]);
int mn = dfs(1, 0, INF), ansp = 0, answ = INF;
for (int i = (mxl + mnr) / 2; i <= (mxl + mnr + 1) / 2; ++i) {
int curw = mn;
if (i < mxl) chkmax(curw, mxl - i);
if (i > mnr) chkmax(curw, i - mnr);
if (curw < answ) answ = curw, ansp = i;
}
write(answ), putchar('\n');
if (tp) {
for (int i = 1; i <= n; ++i) {
if (lp[i] <= ansp && ansp <= rp[i])
ans[i] = ansp;
else if (ansp < lp[i])
ans[i] = lp[i];
else if (rp[i] < ansp)
ans[i] = rp[i];
write(ans[i]), putchar(" \n"[i == n]);
}
}
}
return 0;
}