1 条题解
-
0
1. 问题分析
我们考虑光线在多层玻璃中的传播过程。当光线射到玻璃上时:
- 一部分直接透射过去()
- 一部分被反射回去()
- 剩余的被吸收()
由于存在反射,光线可能在两层玻璃之间来回反射,形成无穷级数。
2. 动态规划思路
设 表示:当 1 单位光从上方垂直射入第 层玻璃时,最终能从第 层玻璃下方穿出的光的总量(包括所有反射路径的贡献)。
我们要求的是 。
2.1 边界条件
对于最后一层玻璃():
- 直接透射:
- 没有反射回来的光(因为后面没有玻璃了)
所以:
2.2 状态转移
考虑第 层玻璃():
- 直接透射:光从第 层上方射入,直接穿过第 层,这部分光为 ,然后这些光会从第 层上方射入,所以能穿出系统的量为:
- 反射路径:光射入第 层后,可能被反射回去(比例 ),然后从第 层下方射入第 层(注意反射改变了方向),在第 层可能又被反射回来(此时从第 层下方反射回上方),再次射到第 层上方,如此循环。
为了处理这个无穷反射,我们引入另一个辅助状态:
设 表示:当 1 单位光从第 层玻璃的下方垂直射入时,最终能从第 层玻璃下方穿出的光总量。
2.3 建立方程
对于 :
- 直接穿过第 层:
- 被第 层反射回去:,然后从第 层下方射入第 层,得到 ,再被第 层反射(从下方反射到上方)比例 ,再射到第 层上方,得到 的贡献。
这个过程形成无穷级数:
设 ,则:
$$X = \frac{a_i}{100} \cdot dp[i+1] + \frac{b_i}{100} \cdot R[i-1] \cdot \frac{b_{i-1}}{100} \cdot X + \frac{b_i}{100} \cdot R[i-1] \cdot \frac{b_{i-1}}{100} \cdot R[i-1] \cdot \frac{b_{i-1}}{100} \cdot X + \dots $$这是一个几何级数:
$$X = \frac{a_i}{100} \cdot dp[i+1] + \frac{b_i}{100} \cdot R[i-1] \cdot \frac{b_{i-1}}{100} \cdot X \cdot \left(1 + \frac{b_{i-1}}{100} R[i-1] + \left(\frac{b_{i-1}}{100} R[i-1]\right)^2 + \dots \right) $$实际上更简单的推导是:
光路:
- 直接透射
- 或者:反射到左边 ,然后从 下方进入得到 ,再反射(从 下方反射到上方)比例 ,再进入第 层上方,此时问题回到 本身。
所以:
$$dp[i] = \frac{a_i}{100} dp[i+1] + \frac{b_i}{100} \cdot R[i-1] \cdot \frac{b_{i-1}}{100} \cdot dp[i] $$整理得:
$$dp[i] \left(1 - \frac{b_i}{100} \cdot \frac{b_{i-1}}{100} \cdot R[i-1]\right) = \frac{a_i}{100} dp[i+1] $$$$dp[i] = \frac{\frac{a_i}{100} dp[i+1]}{1 - \frac{b_i}{100} \cdot \frac{b_{i-1}}{100} \cdot R[i-1]} $$
2.4 类似地求
表示 1 单位光从第 层下方射入时,最终从第 层下方穿出的光总量。
- 直接透射(向上方向):,然后从第 层下方进入得到 。
- 或者:反射(向下方向),然后从第 层上方进入得到 ,再反射(从 上方反射到下方)比例 ,再进入第 层下方,回到 本身。
所以:
$$R[i] = \frac{a_i}{100} \cdot R[i-1] + \frac{b_i}{100} \cdot dp[i+1] \cdot \frac{b_{i+1}}{100} \cdot R[i] $$整理得:
$$R[i] \left(1 - \frac{b_i}{100} \cdot \frac{b_{i+1}}{100} \cdot dp[i+1]\right) = \frac{a_i}{100} R[i-1] $$$$R[i] = \frac{\frac{a_i}{100} R[i-1]}{1 - \frac{b_i}{100} \cdot \frac{b_{i+1}}{100} \cdot dp[i+1]} $$
2.5 边界条件
- (因为第 0 层不存在,光从第 1 层上方进入前,如果从第 0 层下方进入,相当于直接进入系统,全透过)
- 注意 也要算:光从第 层下方进入,直接透射(向上)到第 层下方 ,没有反射到右边的路径(因为右边是外部),所以:
3. 模运算处理
所有分数在模 下计算,需要用到模逆元。
$\frac{p}{100} \equiv p \times \text{inv}(100) \pmod{M}$。
4. 算法步骤
- 预处理
- 初始化
- 初始化
- 从 到 计算:
- 先算 (需要 已知)
- 再算 (需要 已知)
- 最后输出
注意 和 可以交替计算。
5. 样例验证
样例 1:
n=2 a1=50, b1=20 a2=80, b2=5计算:
: 分母 $= 1 - (20\times inv100) \times (5\times inv100) \times dp[2]$ 先算 , 乘积乘 得 ... 最后
再算 $dp[1] = (50\times inv100 \times dp[2]) / (1 - (20\times inv100) \times (5\times inv100) \times R[0])$最终 $dp[1] = 40 \times 100^{-1} / (1 - (20\times 100^{-1}) \times (5\times 100^{-1}))$
即
$40 \times 99^{-1} \bmod M = 40 \times 81818182 \bmod M = 858585865$,符合样例。
6. 代码实现(C++)
#include <bits/stdc++.h> using namespace std; const int MOD = 1e9 + 7; const int MAXN = 500005; int n; int a[MAXN], b[MAXN]; int dp[MAXN], R[MAXN]; int modpow(int a, int e) { int res = 1; while (e) { if (e & 1) res = 1LL * res * a % MOD; a = 1LL * a * a % MOD; e >>= 1; } return res; } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d%d", &a[i], &b[i]); } int inv100 = modpow(100, MOD - 2); dp[n] = 1LL * a[n] * inv100 % MOD; R[0] = 1; // 先计算 R[n] R[n] = 1LL * a[n] * inv100 % MOD * R[n-1] % MOD; for (int i = n - 1; i >= 1; i--) { // 计算 R[i] int denomR = (1 - 1LL * b[i] * inv100 % MOD * b[i+1] % MOD * inv100 % MOD * dp[i+1] % MOD + MOD) % MOD; R[i] = 1LL * a[i] * inv100 % MOD * R[i-1] % MOD * modpow(denomR, MOD - 2) % MOD; // 计算 dp[i] int denomD = (1 - 1LL * b[i] * inv100 % MOD * b[i-1] % MOD * inv100 % MOD * R[i-1] % MOD + MOD) % MOD; dp[i] = 1LL * a[i] * inv100 % MOD * dp[i+1] % MOD * modpow(denomD, MOD - 2) % MOD; } printf("%d\n", dp[1]); return 0; }
7. 复杂度分析
- 时间复杂度:,因为每个状态计算需要模逆元(快速幂)。
- 空间复杂度:。
这样我们就得到了一个可以处理 的高效解法。
- 1
信息
- ID
- 4916
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者