1 条题解
-
0
自动搬运
来自洛谷,原作者为

xhgua
如果只转身后退就能回到那个夏天搬运于
2025-08-24 23:02:07,当前版本为作者最后更新于2024-08-15 20:44:29,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
我们考虑对于一次操作的 ,不妨设 ,则 这条边一定被加上了 ,证明显然。
于是我们可以考虑这样一个做法:每次给 的答案加上 ,然后以相同的概率跳到 的其中一个父亲,同时令 ,此时该问题被我们递归到了一个规模更小的子问题。
发现这个形式十分像 dp,我们不妨将一次操作先挂在 上, 次操作结束后,我们对 进行 dp,设当前状态为 ,枚举 的父亲 ,有转移:
$$g_{u, k} \leftarrow g_{u, k} + \dfrac{g_{u, v}}{r_v - l_v + 1} $$这里同样钦定 ,若 需要将 改为 ,时间复杂度 。
最后点 的答案即为 。
我们考察这个转移形式,发现相当于是给 数组的一段 L 形区域加上了一个固定值,使用二维差分优化即可,时间复杂度 。
结合代码可能比较好理解。
#include <bits/stdc++.h> using i64 = long long; constexpr int N = 5e3 + 5, P = 998244353; using Z = ModInt<P>; int n, m; int l[N], r[N]; Z g[N][N], inv[N], ans[N]; int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); std::cin >> n; for (int i = 2; i <= n; i++) std::cin >> l[i] >> r[i]; for (int i = 1; i <= n; i++) inv[i] = Z(1) / i; auto add = [&](int l1, int r1, int l2, int r2, Z val) -> void { g[r1][r2] += val; g[l1 - 1][r2] -= val; g[r1][l2 - 1] -= val; g[l1 - 1][l2 - 1] += val; }; std::cin >> m; for (int i = 1; i <= m; i++) { int u, v, w; std::cin >> u >> v >> w; if (u == v) continue; if (u > v) std::swap(u, v); add(u, u, v, v, w); } for (int j = n; j >= 1; j--) { for (int i = n; i >= 1; i--) { g[i][j] += g[i + 1][j] + g[i][j + 1] - g[i + 1][j + 1]; // k\in [l, r], i // k <= i, g[k][i] += g[i][j] * inv[r[j] - l[j] + 1]; // k > i, g[i][k] += g[i][j] * inv[r[j] - l[j] + 1]; if (i < j) { if (i >= l[j]) add(l[j], std::min(r[j], i), i, i, g[i][j] * inv[r[j] - l[j] + 1]); if (i <= r[j]) add(i, i, std::max(l[j], i), r[j], g[i][j] * inv[r[j] - l[j] + 1]); } } } for (int i = 1; i <= n; i++) for (int j = i + 1; j <= n; j++) ans[j] += g[i][j]; for (int i = 2; i <= n; i++) std::cout << ans[i] << " "; return 0; }
- 1
信息
- ID
- 9951
- 时间
- 1500ms
- 内存
- 512MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者