摘自gzz学长的讲义。
线性性
X, Y为两随机变量
E(X + Y) = E(X) + E(Y)
做一些期望题时,我们通常把题目所求的分开算期望,最后加一块,这样能大大降低难度。
例一:TC SRM 595 Div1 Problem 900: Constellation
- 题意
n个点,每个点有⼀定概率出现,问凸包的期望⾯积。⽆重复点和三点共线。
n<=50。
- 解析
前置技能:(计算几何)三角剖分求面积
枚举每一条边所代表的面积对答案的贡献(其右侧点不能背选),然后加一块。
例二:P1850 换教室
- 题意
有n堂课需要按顺序听,每堂课有两个教室可以选。
所有的教室都在⼀个连通带权⽆向图上。
下了⼀节课,必须要从这节课所在的教室赶往下节课所在的教室。
⼀开始这n堂课已经选好了默认的教室。
你可以提出最多m个申请,申请把某堂课的教室换到另⼀个。
申请有成功和失败的可能,会给出成功率。
必须⼀次性提出所有申请,不能⽤之前的申请结果来决定之后的申请策略。
最⼩化需要跑的路程的期望。
n ≤ 2000,m ≤ 2000,教室数⽬≤ 300
- 解析
比较不错的期望dp练手题,可以加深对期望的理解。分九种情况大力讨论即可。
值得注意的是,如果当前申请,那么不能是
当前申请成功率 * min(上回申请, 上回不申请) + 当前申请失败率 * min(上回申请, 上回不申请)
因为当前申请时不知道能不能成功,不能探知上回申请和不申请哪一个更优,因此不能同时乘上上回的最优答案,应该拆成
min(当前申请且上回申请, 当前申请且上回不申请)
即比较暴力的分类讨论。
例三:TC SRM 561 Div1 Problem 1000: Orienteering
- 题意
给⼀棵不超过2500个点的树,树上有不超过300个关键点,
从这些关键点中随机选择k个,
然后你任意从⼀个点出发,⽤最短的路程遍历所有的这k个点,
问你的路程的期望⻓度。
- 解析
需要一些小技巧。
容易看出要建虚树。
固定选点,答案为 边长和的二倍 - 直径长。
我们分开来考虑。
边长和二倍比较简单,树形dp可做。
直径长也可以枚举直径端点,算概率,进而算期望。
直径万一有好几条怎么办?我们可以给边权加第二维“扰动”,把每条边加上个0.0000000001 * rand(),这样就很难有多条直径了。对于这道题,用浮点数会稍微麻烦些,可以用pair代替。
这种方法同样适用于其他题中,如计算几何中加个 eps 防止有三点共线 or 两点横/纵坐标相同。但要把握好度,别影响正确性。
随机游走
求从开始到结束的期望步数
-
做法1:设f[a]为从s到a期望走了多少步,答案即为f[t]。关系:f[s]=0,f[a]枚举所有a的前驱,乘以转移到a的概率并求和。
-
做法2:设f[a]为从a到t期望走了多少步,答案即为f[s]。关系:f[t]=0,f[a]枚举所有a的后继,乘以a向后转移的概率并求和。
第一种符合“走路”的思考方式,但是实际仔细思考一下大有问题:
啥叫从起点到i的期望步数?
从起点到i又不是一个只发生一次的事件,甚至从起点到起点自己的期望步数都不能想当然地设成0。
那如果设成“第一次到达”呢?那转移又说不通了,融合在点a,b中的“第一次到达的步数期望”有一部分是在历史上已经到达过c了,自然不可以。
所以整个解释都是浆糊的。
所以用做法2。
我们解决了列方程的问题,接下来,
如果原图是DAG,方程就是可以直接在DAG上拓扑序DP递推出来;
如果原图不是DAG,比如是个有环的有向图,或者甚至是个无向图,那就需要实打实的解这个方程了,用高斯消元。
求经过每个点的期望次数
这个和上一种问题完全不一样了。
想知道点x的经过的期望次数,知道x的后继的经过期望次数没有用了,有用的是知道x的前驱的经过期望次数。
所以这个是从起点开始的。
注意这个方向和上一问是反的,枚举的是前驱不是后继。
多个结束节点,问在每个结束节点结束的概率
我们可以写出概率转移矩阵,
将每个时刻的概率分布转移到下⼀时刻的概率分布,然后求它的⽆穷次⽅。
更好的做法是转化成之前的“期望次数”的问题。
我们转而求解这样一个问题:
从某个起点出发,到每个结束节点时就会停下来,
问最终停下来以后经过每个点的期望次数,
而由于结束节点只会被经过一次,所以它的经过期望次数就是最后在它那里结束的概率。
所以使用上一节的解法即可,
注意枚举的时候把所有的从终点出发的边都删了。
例题
难度单调不降排序。
P4316 绿豆蛙的归宿
随机游走的模板题,与随机游走的第一种题相同。注意,要从终点倒着dp。
将另一篇博客搬了过来。
题意:
给一张有源汇(暂且这么叫)的DAG,边有边权。
每个出度为d的节点有1/d的概率走向其任意一条出边。
求源点到汇点的期望路径长。
n <= 1e5, m <= 2e5
如果按正图建的话,是不对的。
我们以建反图的思路来做这道题。
定义f[i]为i->n的期望路径长,这样有(正图中)f[u] = Σ(u->v) ( (f[v] + len(u->v)) * P(u->v) )
让后利用反图拓扑序解决此问题。
补充:为什么正推不对?
要非得用正推,只好f[i]为1->i的期望。正反对比如下图(左逆推右顺推)(最下面的字母不用管):
这样,我们不得不记录起点到当前点i的概率P[i],不是那么好做。(但好像也可做)。
具体可见:题解 P4316 【绿豆蛙的归宿】
//p,d初始时都是出度
while (front < rear) {
cur = que[++front];
for (register int i = head[cur]; i; i = e[i].nxt) {
to = e[i].to;
f[to] += 1.0 * (f[cur] + e[i].val) / p[to];
if (!(--d[to])) que[++rear] = to;
}
}
P4206 [NOI2005]聪聪与可可
这道题看起来似乎很不可做的样子,有两个点在无向图上走,和三种模型都不太一样。然而我们发现:
-
两个点只有一个点在随机游走,另一个点的行走路线在该点固定的情况下是一定的。
-
由于猫的走法绝对最优,并且可以连走两次,所以不会出现一直走不完的情况,也不会出现状态的环。
于是,我们将状态设为f[u][v],表示猫在u,老鼠在v的期望步数。然后预处理出dis数组和nxt数组,记忆化搜索即可。
Code:
double dfs(int u, int v) {
if (f[u][v]) return f[u][v];
if (u == v) return 0;
if (dis[u][v] <= 2) return f[u][v] = 1;
int nw = nxt[u][v];
nw = nxt[nw][v];
int to;
for (register int i = head[v]; i; i = e[i].nxt) {
to = e[i].to;
f[u][v] += (dfs(nw, to) + 1) / (d[v] + 1);
}
f[u][v] += (dfs(nw, v) + 1) / (d[v] + 1);
return f[u][v];
}
P3232 [HNOI2013]游走
-
问题转化:给一个无向连通图,1 -> n,等概率游走。求每一条边的期望经过次数g[i]。
-
解析:
可转化成求点的期望经过次数f[i]。
(d为度数)
不是DAG,需要高斯消元
注意n是终点,不要算它的f。
(Code):
inline void gx() {//高斯消元
for (register int i = 1; i <= n; ++i) {
int mx = i;
for (register int j = i + 1; j <= n; ++j)
if (F(a[j][i]) > F(a[mx][i])) mx = j;
if (F(a[mx][i]) <= eps) continue;
for (register int j = 1; j <= n; ++j) {
if (j == mx) continue;
double k = a[j][i] / a[mx][i];
for (register int p = 1; p <= n + 1; ++p)
a[j][p] -= a[mx][p] * k;
}
for (register int p = 1; p <= n + 1; ++p)
swap(a[mx][p], a[i][p]);
}
for (register int i = 1; i <= n; ++i)
f[i] = a[i][n + 1] / a[i][i];
}
...
for (register int cur = 1; cur < n; ++cur) {
a[cur][cur] = 1;
register int to;
for (register int i = head[cur]; i; i = e[i].nxt) {
to = e[i].to;
if (to == n) continue;
a[cur][to] = -1.0 / d[to];
}
}
a[1][n + 1] = 1;
gx();
for (register int i = 1; i <= ecnt; i += 2) {
if (e[i].from != n) g[i/2] += f[e[i].from] / d[e[i].from];
if (e[i].to != n) g[i/2] += f[e[i].to] / d[e[i].to];
}
CF446D DZY Loves Games
首先分层图,掉血即为进入下一层。目标:((1,K)) -> ((n, 1)) 的概率。
然后就成了“从一个点出发,最终到另一个点的概率”。(O((nK)^3))即可完成此题。
发现分层图可以分层高消,(O(n^3K))即可完成此题。
然而 (1e9) 的 (K) 总是那么烦人。发现每层都是相似的,因此可以直接高消记录一下 (i -> j) 的概率。然而 (500) 限制了 (n^3log),并且如果 (j) 不是掉血点,就难以用“期望经过次数”来代替“概率”了。因此,考虑“缩点”,即计算出关键点到关键点的概率。注意到以不同的点作为起点,方程只是在系数上有变化,因此可以用高消的时候带着系数一起消的方法来迅速求出。这种方法类似矩阵的逆。
最后要注意:这里不能理所当然的设置起点的方程为:
因为我们要求的是从 S 出来以后在经过 (S) 的期望次数,不能上来给个1.因此需要把这个1分到与之连边的那些点上。
其他例题:
bzoj1444 [Jsoi2009]有趣的游戏
TC SRM 641 BitToggler
almost all by gzz
还要多做题啊。