题解: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;
}
本文来自博客园,作者:Orange_new,转载请注明原文链接:https://www.cnblogs.com/JPGOJCZX/p/18834576

浙公网安备 33010602011771号