【学习笔记】随机游走优化 dp
2026-04-15 14:54:58
发布于:山东
感觉是比较冷门的技巧()
引入
一个数轴,初始有一个点在 位置。现在这个点会移动 次,每一次有 的概率点从 移动到 ,另外 的概率点从 移动到 。问移动完 次之后 的期望值是多少。
上述问题的答案不超过 级别。下面给出一个简单的证明:
直接计算 比较复杂。考虑先放缩一下,计算出 的值,然后就可以直接得到 。
记第 步 移动的决策是 (也就是 在第 次操作中移动到了 位置)。则显然有 ,也就可以得到 。又因为决策之间是两两独立的,所以直接分类 的取值分别算期望,可以得到 ,也就得到了 。
现在在上面的问题上扩展,考虑另外一个与其相似的问题:
一个数轴,初始有一个点在 位置。现在这个点会移动 次,每一次有 的概率点从 移动到 ,另外 的概率点从 移动到 。问移动完 次后, 移动到的全部 个位置中绝对值最大的点的期望值是多少。
解决完上一个问题之后容易猜测答案还是 级别的。下面给出一个证明:
设随机游走位置为 ,其中每个 有 的概率为 ,另外 的概率为 。为了方便,记 即前 次移动中距离原点最远的距离是多少。
因为 是非负整数,所以 。
所以只要我们能证明 ,把这个式子对 求和,就会得到 。
事件 的意思是:在前 步里,曾经到过 或者 。所以 。
由于对称性,这两项相等,因此有:。
现在用一维随机游走最经典的“反射法”结论:,结合一下就可以得到 。
此时又因为 是 个独立 的和,所以它的方差是 ,标准差是 。更进一步, 有高斯型尾估计:,因此 。
结合一开始得到的式子,有:
而这个和的量级就是 。最简单的看法是把它和积分比较:
所以有 。于是得到最终结论:
事实上,可以继续收紧上下界得到 ,但是我不会证,而且在实际题目中并不需要这么紧的上界,因此这里直接给出一个由 ChatGPT 5.4 Thinking 给出的证明过程:
:::info[证明过程]
设随机游走的过程为:
题目要求的是 的期望 。
1. 先写成尾和公式
因为 是非负整数值随机变量,所以:
而 步最多走到距离 ,所以实际上:
也就是:
2. 精确公式
事件 就是“前 步始终没有碰到 ”,也就是随机游走在区间
内存活到第 步。
这是经典吸收随机游走,谱分解可得:
因此:
这就是这个期望的一个标准精确表达式。
3. 渐近结果
当 时,有经典极限定理:
其中 是标准布朗运动。并且该极限随机变量的期望是:
所以:
也就是主项约为:
4. 对比一下末位置
注意这不是最后位置 的期望。后者是 ,而这里取的是全过程的最大绝对值,所以更大,主常数变成了 。
5. 最终结论
精确值:
渐近值:
:::
解决问题
考虑用上面给出的 trick 解决一道经典题目!
题目给出的六边形网格不太好处理,因此容易想到把这个东西压扁,将六个方向修改为上,下,左,右,右上,左下。此时容易想到直接 dp。设 表示当前处理了前 个 idea,,当前网格的横坐标是 ,纵坐标是 ,是否是可行的状态。
考虑优化这个 dp。可行性 dp 通常有下面两种优化方法:
- 把一维状态写到答案里。
- 用 bitset 优化。
这个问题看着很不能把状态写到答案里,因此考虑用 bitset 优化。发现 这个 dp 维度其实就是在做一次循环移位操作,用 bitset 优化可以让时间复杂度除一个 ,但是仍然难以通过。
考虑利用上面的 trick。注意到最后需要让所在的位置 回到 ,而上面的 trick 在更高维度上也同样满足距离 的曼哈顿距离期望为 级别。因此考虑直接随机打乱输入的 idea 顺序,此时对于距离原点超过 的位置,其很大概率是可以被不超过 的位置所替代的,因此只需要处理 的部分,范围以外的 dp 值直接截断处理即可。此时算法的时间复杂度被优化到 ,卡常后可以通过该题。
:::success[代码]
因为作者不太会卡常所以目前只有用 C++98 提交才能通过/kel
#include <bits/stdc++.h>
using namespace std;
const int N = 400010;
const int mod = 998244353;
bitset<26> f[2][110][110][26];
int p[N], a[N][12];
signed main() {
cin.tie(0)->sync_with_stdio(false);
int n, M; cin >> n >> M;
const int m = M, st = sqrt(n) + 2;
for (int i = 1; i <= n; ++i)
for (int j = 0; j < 12; ++j) cin >> a[i][j];
int x, y; cin >> x >> y;
for (int i = 1; i <= n; ++i) p[i] = i;
random_shuffle(p + 1, p + n + 1);
f[0][0][0][st] = 1 << st;
for (int I = 1, i = p[1]; I <= n; i = p[++I])
for (int j = 0; j < m; ++j)
for (int k = 0; k < m; ++k)
for (int h = 0; h <= st * 2; ++h) {
f[I & 1][j][k][h] = f[~I & 1][(j - a[i][4] + m) % m][(k - a[i][5] + m) % m][h + 1] >> 1 | f[~I & 1][(j - a[i][0] + m) % m][(k - a[i][1] + m) % m][h] << 1 | f[~I & 1][(j - a[i][2] + m) % m][(k - a[i][3] + m) % m][h + 1] | f[~I & 1][(j - a[i][6] + m) % m][(k - a[i][7] + m) % m][h] >> 1;
if (h) f[I & 1][j][k][h] |= f[~I & 1][(j - a[i][8] + m) % m][(k - a[i][9] + m) % m][h - 1] | f[~I & 1][(j - a[i][10] + m) % m][(k - a[i][11] + m) % m][h - 1] << 1;
}
cout << ((f[n & 1][x][y][st][st]) ? "Chaotic Evil" : "Not a true problem setter") << '\n';
return 0;
}
:::
练习
给定一个 个结点的树,每条边都有边权(边权可能为负)。你需要选择若干条有 条边组成的简单路径(可以不选),使得没有一条边被超过一条路径覆盖。问所有被路径覆盖的边的边权的和最大是多少。
数据范围:。
考虑一个暴力的 dp 做法。设 表示当前只处理 结点为根的子树, 结点没有挂长度不为 的链 / 在子树里挂了一条长度为 的链,边权之和最大是多少。转移过程需要合并儿子结点的 dp 信息,考虑再记一个 dp 数组辅助转移:设 表示当前合并了若干个儿子结点的信息,其中儿子结点里长度为 的链比长度为 的链多 条,长度为 的链数量 ,边权和最大是多少(只记录这些信息是因为子树内合并链只能是长度为 的链对应匹配,长度为 的链单独匹配)。此时合并信息是简单的。
直接做转移时间复杂度为 。注意到辅助转移的 数组中 维度维护的是两类儿子结点的链的差值,而一个儿子结点只能有最多一条连向父亲结点的链,最后可以转移到 数组里的 也只有 三类。因此考虑把长度为 的链看作 ,长度为 的链看作 ,则可以看作是在数轴上做随机游走,打乱儿子结点顺序后 这个维度只需要维护 个 的信息即可。此时算法的时间复杂度优化到 ,可以通过该题。
:::success[代码]
// #pragma GCC optimize(3, "Ofast", "inline", "unroll-loops")
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#define int long long
using namespace std;
const int N = 200010;
const int mod = 998244353;
const int inf = 1e18;
constexpr inline void add(int &x, int v) { x += v; if (x >= mod) x -= mod; }
constexpr inline void sub(int &x, int v) { x = (x + mod - v) % mod; }
vector<pair<int, int>> adj[N];
int f[N][4], g[2][1010][2];
inline void dfs(int u, int fa) {
for (auto &[v, w] : adj[u]) if (v != fa) dfs(v, u);
memset(g, -0x3f, sizeof g);
int l = 500, r = 500, o = 0; g[0][500][0] = g[1][500][0] = 0;
for (auto &[v, w] : adj[u]) if (v != fa) {
--l, ++r, o ^= 1;
if (l < 1) l = 1;
if (r > 1000) r = 1000;
for (int i = l; i <= r; ++i)
for (int j = 0; j < 2; ++j)
g[o][i][j] = max({
f[v][0] + g[o ^ 1][i][j],
f[v][0] + g[o ^ 1][i - 1][j] + w,
f[v][1] + g[o ^ 1][i][j ^ 1] + w,
f[v][2] + g[o ^ 1][i + 1][j] + w,
f[v][3] + g[o ^ 1][i][j] + w
});
} f[u][0] = g[o][500][0], f[u][1] = g[o][501][0], f[u][2] = g[o][500][1], f[u][3] = g[o][499][0];
}
signed main() {
cin.tie(0)->sync_with_stdio(false);
int n; cin >> n;
for (int i = 1; i < n; ++i) {
int a, b, c; cin >> a >> b >> c;
adj[a].emplace_back(b, c), adj[b].emplace_back(a, c);
} for (int i = 1; i <= n; ++i) random_shuffle(adj[i].begin(), adj[i].end());
dfs(1, 0), cout << f[1][0] << '\n';
return 0;
}
:::
全部评论 165
ddd
2026-04-15 来自 山东
34d
2026-04-16 来自 浙江
21d
2026-04-16 来自 浙江
23d
2026-04-16 来自 浙江
21
q
2026-04-15 来自 浙江
15p
2026-04-22 来自 浙江
1
ddd
2026-04-15 来自 山东
12ddd
2026-04-20 来自 浙江
3ddd
2026-04-20 来自 浙江
3ddd
2026-04-20 来自 浙江
2
66666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666
2026-04-17 来自 北京
966
2026-04-22 来自 浙江
0666
2026-04-22 来自 上海
0999
1周前 来自 四川
0
这……这对吗?
2026-04-17 来自 广东
7我和“第五特殊小队——"夜幕"”的小伙伴都在ACGO等你,快用这个专属链接加入我们吧!https://www.acgo.cn/application/2038947898070085632
2026-04-18 来自 浙江
6点不开
2026-04-20 来自 山东
1?
2026-04-20 来自 浙江
0https://www.acgo.cn/application/2038947898070085632,复制最后的这个,就会跳出一个“打开链接”,点一下就行了
2026-04-20 来自 浙江
0
h
2026-04-16 来自 浙江
5看不懂
2026-04-18 来自 浙江
44
2026-04-18 来自 浙江
43
2026-04-18 来自 浙江
4有互关的吗,我是年糕
2026-04-18 来自 江苏
35
2026-04-18 来自 浙江
34
2026-04-18 来自 浙江
32
2026-04-18 来自 浙江
3没看懂

2026-04-18 来自 浙江
3ddd
2026-04-15 来自 浙江
3ddd
2026-04-15 来自 山东
3666
2026-04-19 来自 上海
2卧槽咋这么数学都这么强,但凡是根数学沾边的我都好菜好菜的(连数位dp都是,计数,数论)
2026-04-18 来自 四川
21
2026-04-18 来自 浙江
2
































































有帮助,赞一个