题解:P10879 「KDOI-07」对树链剖分的爱

节选自:DP做题记录(三)(2025.4.5 - 2025.4.19)

我们考虑通常如何把一条树链 \((u, v)\) 增加 \(w\) 的权值,最暴力的做法就是把 \(u\)\(v\) 分别到根的路径上的边权加 \(w\),再将 \(u\)\(v\) 的 LCA 到根的路径上的边权减 \(2 \times w\)。其实可以发现,不管 \(u\)\(v\) 是否是祖先关系,它们中深度更深的点的父边一定加上了 \(w\)。而且这道题目有一个有趣的性质,那就是一个点 \(u\) 的父亲一定比 \(u\) 的编号小,那也就是 \(u, v\) 中编号更大的点的父边一定加上了 \(w\)

推出了性质后,我们从简单情况一步一步分析。假设只有一组修改 \((u, v, w)\),那么我们直接设 $ dp_{i, j}(i \geq j)$ 表示从点对 \((u, v)\) 往上走到 \((i, j)(u \leq i, j \leq v)\)\(i\) 的父边权值的期望,由于推出的性质,只有 \(i\) 的父边一定加上了 \(w\),因此可以很轻松写出转移方程,枚举 \(fa_i \in [l_i, r_i]\),那么若 \(fa_i \geq j\)\(f_{fa_i, j} = \displaystyle\frac{w}{r_i - l_i + 1}\);若 \(fa_i < j\)\(f_{j, fa_i} = \displaystyle\frac{w}{r_i - l_i + 1}\)

我们考虑期望的线性性,两个独立事件的期望和,等于这两个事件至少发生一件的期望,那么由于这几个加法操作相互独立,它们的期望可以直接加在一起,于是改变 \(f_{i, j}(i \geq j)\) 的含义,为所有能够往上走到 \((i, j)\) 的所有点对 \((u, v)\),从 \((u, v)\) 往上走到 \((i, j)\)\(i\) 的父边权的期望的和,转移方程几乎不变,只是把赋值变成加操作。此时一个点 \(u\) 的答案就是 \(\displaystyle\sum_{u > v} dp_{v, u}\)。不过这样时间复杂度是 \(O(n^3)\),还是过不了。

我们考虑转移方程,对于所有 \(fa_i \geq j\)\(f_{fa_i, j} = \displaystyle\frac{w}{r_i - l_i + 1}\),此时 \(j\) 是不变的,而 \(fa_i\) 在区间 \([j, r_i]\) 中,这相当于是对平面上一个 \(1 \times (r_i - j + 1)\) 的矩阵加上了 \(w\);同理,对于所有 \(fa_i < j\),这相当于是对平面上一个 \((j - l_i) \times 1\) 的矩阵加上了 \(w\),由于是把所有操作都执行完后才进行询问,于是可以用二维差分将时间复杂度优化成 \(O(n^2 + m)\),足以通过此题。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e3 + 9, MOD = 998244353;
int f[N][N], s1[N][N], s2[N][N], inv[N], l[N], r[N], ans[N], n, m;
void init(){
	inv[1] = 1;
	for(int i = 2; i < N; i++)
		inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
}
signed main(){
	init();
	scanf("%lld", &n);
	for(int i = 2; i <= n; i++)
		scanf("%lld%lld", &l[i], &r[i]);
	scanf("%lld", &m);
	for(int i = 1; i <= m; i++){
		int u, v, w;
		scanf("%lld%lld%lld", &u, &v, &w);
		if(u < v)
			swap(u, v);
		f[u][v] = (f[u][v] + w) % MOD;
	}
	for(int i = n; i >= 1; i--){
		for(int j = i - 1; j >= 1; j--){
			s1[i][j] = (s1[i][j] + s1[i + 1][j]) % MOD;
			s2[i][j] = (s2[i][j] + s2[i][j + 1]) % MOD;
			f[i][j] = (f[i][j] + s1[i][j] + s2[i][j]) % MOD;
			ans[i] = (ans[i] + f[i][j]) % MOD;
			int num = f[i][j] * inv[r[i] - l[i] + 1] % MOD;
			if(j < l[i]){
				s1[r[i]][j] = (s1[r[i]][j] + num) % MOD;
				s1[l[i] - 1][j] = ((s1[l[i] - 1][j] - num) % MOD + MOD) % MOD;
			} else if(j > r[i]){
				s2[j][r[i]] = (s2[j][r[i]] + num) % MOD;
				s2[j][l[i] - 1] = ((s2[j][l[i] - 1] - num) % MOD + MOD) % MOD;
			} else {
				s2[j][j] = (s2[j][j] + num) % MOD;
				s1[r[i]][j] = (s1[r[i]][j] + num) % MOD;
				s2[j][l[i] - 1] = ((s2[j][l[i] - 1] - num) % MOD + MOD) % MOD;
				s1[j][j] = ((s1[j][j] - num) % MOD + MOD) % MOD;
			}
		}
	}
	for(int i = 2; i <= n; i++)
		printf("%lld ", ans[i]);
	return 0;
}
posted @ 2025-04-18 21:03  Orange_new  阅读(16)  评论(0)    收藏  举报