题解:P14135 【MX-X22-T6】「TPOI-4F」Miserable EXperience

题意

给定一棵以 \(1\) 为根的 \(n\) 个点的树,点 \(i\) 的点权为 \(a_i\)。你可以进行两种操作:

  1. 选择一个点 \(u\),将 \(sub_u\) 内所有点的点权减 \(1\)
  2. 选择一个数 \(x\),将所有深度为 \(x\) 的点的点权减 \(1\)

对于每个点 \(u\),求出将 \(sub_u\) 内所有点的点权变为 \(0\) 的最小操作次数,或报告无解。\(1\leq n\leq 10^6\)

题解

首先考虑一整棵树怎么做。

先树上差分,令 \(a_u\gets a_u-a_{fa_u}\)。设这棵树的最大深度为 \(D\),则题述操作变成了:

  1. 选择一个点 \(u\),令 \(a_u\gets a_u-1\)
  2. 选择一个数 \(x\),令所有深度为 \(x\) 的点的点权减 \(1\)。若 \(x<D\),则再令所有深度为 \(x+1\) 的点的点权加 \(1\)

考虑将所有 \(1\) 操作提前,那么我们要求执行完所有 \(1\) 操作后,同深度的点的点权相同。对于每一个深度 \(x\),设该层的点权最小值为 \(mn_x\),那我们一定是对于该层的每一个点,用操作 \(1\) 把它的点权减到 \(mn_x\)。显然这一步的开销是不可避免的。

执行上述操作后,每一层的点权相同,我们可以转成序列问题。设深度为 \(x\) 的节点有 \(cnt_x\) 个,此时两种操作变成:

  1. 选择一个位置 \(i\),令 \(a_i\gets a_i-1\)。代价为 \(cnt_i\)
  2. 选择一个位置 \(i\),令 \(a_i\gets a_i-1\)。若 \(i<D\),则再令 \(a_{i+1}\gets a_{i+1}+1\)。代价为 \(1\)

显然只有操作 \(2\) 能处理负点权,我们先倒序枚举 \(i=D,\cdots,2\),若 \(a_i<0\),则对位置 \(i-1\) 执行 \(-a_i\) 次操作 \(2\)。如果最后 \(a_1<0\) 则无解。

现在保证了点权是非负的,这个问题性质就很好了。可以发现每个位置上的每个单位都是独立的,也就是考察 \(i\) 上的一个单位,我们要么用 \(c_i\) 的代价将其消去,要么用 \(1\) 的代价把它转化成 \(i+1\) 上的一个单位。直接 DP,令 \(f_i\) 表示消去 \(i\) 上的一个单位的最小代价,转移方程很简单:

\[f_i=\min(c_i,f_{i+1}+1) \]

最终答案就是 \(\sum\limits_{i=1}^Df_ia_i\)。至此我们得到了 \(\mathcal{O}(n^2)\) 的做法。

考虑如何优化。注意到我们维护的信息都是深度相关,考虑长剖维护。具体来说,我们需要维护:

  • \(cnt_x,mn_x\) 表示深度为 \(x\) 的点的个数、点权最小值。
  • \(sum_x\) 表示深度为 \(x\) 的点为了减到 \(mn_x\) 所花费的总代价。
  • \(d_x\) 表示深度为 \(x\) 的点为了变成非负数所产生的偏移量。
  • \(f_x\) 表示深度为 \(x\) 处的 DP 值。

\(cnt\)\(mn\) 是容易继承与合并的。对于后面三个信息,继承长链的信息只会在深度为 \(0/1\) 处产生影响,而合并一个轻儿子只需要在其对应深度范围内暴力重构即可。时间复杂度为 \(\mathcal{O}(n)\)

实现上细节比较多,参考了官方题解的实现。

主要代码
void dfs1(int x) {
	for (int i = T.head[x]; i; i = T.nxt[i]) {
		int y = T.to[i];
		dfs1(y);
		if (h[y] > h[son[x]]) son[x] = y;
	}
	h[x] = h[son[x]] + 1;
}
void dfs2(int x) {
	dp[x][0] = cnt[x][0] = 1, ans[x] = mnv[x][0] = a[x];
	if (son[x]) {
		dp[son[x]] = dp[x] + 1, cnt[son[x]] = cnt[x] + 1, mnv[son[x]] = mnv[x] + 1, d[son[x]] = d[x] + 1;
		sum[son[x]] = sum[x] + 1;
		dfs2(son[x]);
		if (~ans[son[x]]) {
			ans[x] = ans[son[x]] - mnv[x][1] - d[x][1];
			mnv[x][1] = a[son[x]] -= a[x];
			if (mnv[x][1] + d[x][1] < 0) {
				ans[x] -= mnv[x][1] + d[x][1];
				d[x][0] = mnv[x][1] + d[x][1], d[x][1] = -mnv[x][1];
			} else ans[x] += mnv[x][1] + d[x][1];
			if (mnv[x][0] + d[x][0] < 0) ans[x] = -1;
			else ans[x] += mnv[x][0] + d[x][0];
		} else ans[x] = -1;
	}
	for (int i = T.head[x]; i; i = T.nxt[i]) {
		int y = T.to[i];
		if (y == son[x]) continue;
		alloc(y), dfs2(y);
		if (ans[x] == -1 || ans[y] == -1) { ans[x] = -1; continue; }
		mnv[y][0] = a[y] -= a[x];
		for (int j = 1; j <= h[y]; ++j) {
			ans[x] -= (ll)dp[x][j] * (mnv[x][j] + d[x][j]) + sum[x][j];
			sum[x][j] += sum[y][j - 1];
			if (mnv[x][j] < mnv[y][j - 1]) sum[x][j] += (ll)cnt[y][j - 1] * (mnv[y][j - 1] - mnv[x][j]);
			else sum[x][j] += (ll)cnt[x][j] * (mnv[x][j] - mnv[y][j - 1]), mnv[x][j] = mnv[y][j - 1];
			cnt[x][j] += cnt[y][j - 1], ans[x] += sum[x][j];
		}
		ans[x] -= (ll)dp[x][0] * (mnv[x][0] + d[x][0]);
		for (int j = h[y]; j; --j) if (mnv[x][j] + d[x][j] < 0) {
			ans[x] -= mnv[x][j] + d[x][j];
			d[x][j - 1] += mnv[x][j] + d[x][j], d[x][j] = -mnv[x][j];
		}
		if (mnv[x][0] + d[x][0] < 0) ans[x] = -1;
		else {
			for (int j = h[y]; j; --j) dp[x][j] = min(cnt[x][j], j == h[x] - 1 ? 1 : dp[x][j + 1] + 1);
			for (int j = 0; j <= h[y]; ++j) ans[x] += (ll)dp[x][j] * (mnv[x][j] + d[x][j]);
		}
	}
}
posted @ 2026-01-13 21:16  P2441M  阅读(9)  评论(0)    收藏  举报