前言
高中数学讲过期望:。但竞赛中的期望题很少直接用定义算——而是反复使用期望的线性性:。这个等式在 不独立时也成立!这意味着我们可以把复杂问题拆成若干简单随机变量之和,分别求期望再相加。
问题的本质
期望的线性性
为什么 不需要独立?因为:
右边就是 。无论 是否独立,这个等式都成立。
追问:那 呢?这需要独立!展开后交叉项无法分离。
概率 DP
很多期望题的递推关系形如:
其中 表示从状态 出发的期望花费。这实际上是解一个线性方程组。当状态转移形成 DAG 时,可以按拓扑序 DP;有环时需要高斯消元。
优惠券收集者问题
种卡片,每次随机抽到一种。集齐全部 种的期望次数?
设 = 从已有 种到有 种所需的抽卡次数。 服从参数为 的几何分布,。
其中 是第 个调和数。
理论 + 代码
模板:独立随机变量的期望之积
当 独立时,。这在”骰子乘积”类问题中直接使用。
// N 个骰子,第 i 个有面值 A[i][0..5],求所有出面的乘积的期望 mod 10^9+7
// 因为每个骰子独立,E[product] = prod E[one die]
long long ans = 1;
for (int i = 0; i < N; i++) {
long long sum = 0;
for (int j = 0; j < 6; j++)
sum = (sum + A[i][j]) % MOD; // ① 单个骰子的期望
ans = ans * sum % MOD; // ② 独立→乘积
ans = ans * power(6, MOD - 2, MOD) % MOD; // ③ 除以 6(乘以逆元)
}
逐行解析:
- ① 第 个骰子的期望 。
- ② 独立随机变量的期望之积 = 期望的积。
- ③ 模意义下除法用逆元。
模板:优惠券收集者
double coupon_collector(int N) {
double ans = 0;
for (int k = 1; k <= N; k++)
ans += 1.0 / k; // ① 调和级数
return ans * N; // ② 乘以 N
}
模板:期望逆序对
个位置,第 个位置的值在 上均匀分布。求期望逆序对数。
由期望的线性性,。每对独立计算。
例题
例题 1:M&A 023 — Dice Expectation(期望线性性)
题目:蓝骰子有面值 ,红骰子有面值 。掷两个骰子,求面值之和的期望。
数据范围:
分析:,由期望的线性性。
输入样例:
3
1 2 3
10 20 30
输出:22.000000000000
验证:,。。✓
代码:
#include <cstdio>
int main() {
int N;
scanf("%d", &N);
double sumB = 0, sumR = 0;
for (int i = 0; i < N; i++) { int x; scanf("%d", &x); sumB += x; }
for (int i = 0; i < N; i++) { int x; scanf("%d", &x); sumR += x; }
printf("%.12f\n", sumB / N + sumR / N); // ① 线性性
}
逐行解析:
- ① ,蓝骰子和红骰子的期望分别计算。
例题 2:T90 052 — Dice Product(独立期望之积)
题目: 个 6 面骰子,第 个骰子的第 面为 。求所有 种出面的乘积之和对 取模。
数据范围:
分析:乘积之和 = (独立性)。每个 。
代码:
#include <cstdio>
const long long MOD = 1000000007;
long long power(long long a, long long b) {
long long res = 1;
while (b > 0) { if (b & 1) res = res * a % MOD; a = a * a % MOD; b >>= 1; }
return res;
}
int main() {
int N;
scanf("%d", &N);
long long ans = 1;
long long inv6 = power(6, MOD - 2); // ① 6 的逆元
for (int i = 0; i < N; i++) {
long long sum = 0;
for (int j = 0; j < 6; j++) { int x; scanf("%d", &x); sum = (sum + x) % MOD; }
ans = ans * sum % MOD; // ② E[product] = prod E[one]
ans = ans * inv6 % MOD; // ③ 除以 6
}
printf("%lld\n", ans);
}
逐行解析:
- ① 预计算 。
- ②③ 乘积的期望 = 期望的乘积。每个 。
验证:样例 1,。骰子 1: {1,2,3,5,7,11}, 和=29。骰子 2: {4,6,8,9,10,12}, 和=49。。。✓
例题 3:M&A 026 — Coin Gacha(优惠券收集者)
题目: 种硬币,每次花 1 元等概率抽一种。集齐全部的期望花费。
数据范围:
分析:标准优惠券收集者。答案 。
代码:
#include <cstdio>
int main() {
int N;
scanf("%d", &N);
double ans = 0;
for (int k = 1; k <= N; k++)
ans += 1.0 / k; // ① 调和级数
printf("%.12f\n", ans * N); // ② 乘以 N
}
逐行解析:
- ① ,调和级数。
- ② 。
验证:。。。✓
例题 4:T90 066 — Various Arrays(期望逆序对)
题目: 个位置,第 个的值在 上均匀随机。求期望逆序对数。
数据范围:,
分析:由期望的线性性,。每对独立计算:
即 的可能值中小于 的个数之和。
代码:
#include <cstdio>
int L[109], R[109];
int main() {
int N;
scanf("%d", &N);
for (int i = 0; i < N; i++) scanf("%d%d", &L[i], &R[i]);
double ans = 0;
for (int i = 0; i < N; i++) {
for (int j = i + 1; j < N; j++) { // ① 每对 (i, j)
double cnt = 0;
for (int x = L[i]; x <= R[i]; x++) {
// P(a_j < x) = max(0, x - L[j]) / (R[j]-L[j]+1)
// 但 a_j < x 即 a_j <= x-1
if (x > L[j]) {
cnt += min(x - 1, R[j]) - L[j] + 1; // ② a_j < x 的可能值数
}
}
double pi = (double)(R[i] - L[i] + 1) * (R[j] - L[j] + 1);
ans += cnt / pi; // ③ P(a_i > a_j)
}
}
printf("%.12f\n", ans);
}
逐行解析:
- ① 枚举所有逆序对 ,。
- ② 对于固定的 , 的可能值数量。
- ③ = 有利情况 / 总情况。
验证:样例 1,。。✓
参考文献
基础练习 — アルゴリズムと数学 演習問題集
- 023 Dice Expectation(期望线性性)【例题】
- 024 Answer Exam Randomly(期望线性性)
- 025 Jiro’s Vacation(期望线性性)
- 026 Coin Gacha(优惠券收集者)【例题】
实战练习 — 競プロ典型 90 問
系列索引
第零章 基础工具
第一章 搜索技术
第二章 数学基础
第三章 数据结构
- 03-01 栈、队列与单调栈
- 03-02 堆与优先队列
- 03-03 并查集
- 03-04 树状数组
- 03-05 线段树
- 03-06 懒标记线段树
- 03-07 Sparse Table 与倍增
- 03-08 字符串哈希
第四章 图论
- 04-01 图的遍历
- 04-02 最短路—Dijkstra 与 01-BFS
- 04-03 最短路—Bellman-Ford 与 Floyd
- 04-04 拓扑排序
- 04-05 最小生成树
- 04-06 强连通分量与 2-SAT
- 04-07 二分图与网络流
- 04-08 树上问题
第五章 动态规划
- 05-01 DP入门—状态与转移
- 05-02 背包问题族
- 05-03 LIS、LCS与编辑距离
- 05-04 区间DP
- 05-05 状态压缩DP
- 05-06 树形DP与数位DP
- 05-07 矩阵快速幂与线性递推
第六章 贪心
第七章 字符串
第八章 进阶