1 条题解
-
0
2670. 「NOI2012」随机数生成器 题解
题目分析
题目给出了线性同余生成器(LCG)的递推公式: [ X_{n+1} = (aX_n + c) \bmod m ]
要求计算 ,其中 最大可达 , 最大可达 。
直接模拟是不可行的,因为 太大。
核心思路
1. 转化为矩阵乘法
观察递推式: [ X_{n+1} = aX_n + c ]
我们可以将其表示为矩阵形式: [ \begin{pmatrix} X_{n+1} \ 1 \end{pmatrix} = \begin{pmatrix} a & c \ 0 & 1 \end{pmatrix} \begin{pmatrix} X_n \ 1 \end{pmatrix} ]
令 ,则有: [ \begin{pmatrix} X_n \ 1 \end{pmatrix} = M^n \begin{pmatrix} X_0 \ 1 \end{pmatrix} ]
2. 快速幂加速
计算 可以使用矩阵快速幂,时间复杂度 。
3. 大数乘法优化
由于 最大可达 ,两数相乘会超过 128 位整数范围(C++ 中的
long long是 64 位)。因此不能直接使用普通的乘法,需要使用 快速乘(龟速乘)。
快速乘(龟速乘)
当计算 时,如果 都接近 ,直接相乘会溢出。快速乘的原理类似于快速幂,将乘法转化为加法:
LL mul(LL a, LL b, LL mod) { LL res = 0; a %= mod; b %= mod; while (b) { if (b & 1) res = (res + a) % mod; a = (a + a) % mod; b >>= 1; } return res; }时间复杂度 ,在本题中是可以接受的。
矩阵快速幂实现
定义矩阵结构体和矩阵乘法:
struct Matrix { LL m[2][2]; }; Matrix mul(Matrix a, Matrix b, LL mod) { Matrix res; for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { res.m[i][j] = 0; for (int k = 0; k < 2; k++) { // 使用快速乘防止溢出 res.m[i][j] = (res.m[i][j] + mul(a.m[i][k], b.m[k][j], mod)) % mod; } } } return res; } Matrix pow_matrix(Matrix a, LL n, LL mod) { Matrix res; // 单位矩阵 res.m[0][0] = res.m[1][1] = 1; res.m[0][1] = res.m[1][0] = 0; while (n) { if (n & 1) res = mul(res, a, mod); a = mul(a, a, mod); n >>= 1; } return res; }
完整算法流程
- 读入
- 构造矩阵
- 计算 使用矩阵快速幂(注意模数是 )
- 计算
- 输出
代码实现
#include <bits/stdc++.h> using namespace std; typedef long long LL; // 快速乘(龟速乘) LL mul(LL a, LL b, LL mod) { LL res = 0; a %= mod; b %= mod; while (b) { if (b & 1) res = (res + a) % mod; a = (a + a) % mod; b >>= 1; } return res; } struct Matrix { LL m[2][2]; }; // 矩阵乘法 Matrix mul_matrix(Matrix a, Matrix b, LL mod) { Matrix res; for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { res.m[i][j] = 0; for (int k = 0; k < 2; k++) { res.m[i][j] = (res.m[i][j] + mul(a.m[i][k], b.m[k][j], mod)) % mod; } } } return res; } // 矩阵快速幂 Matrix pow_matrix(Matrix a, LL n, LL mod) { Matrix res; // 单位矩阵 res.m[0][0] = res.m[1][1] = 1; res.m[0][1] = res.m[1][0] = 0; while (n) { if (n & 1) res = mul_matrix(res, a, mod); a = mul_matrix(a, a, mod); n >>= 1; } return res; } int main() { LL m, a, c, x0, n, g; cin >> m >> a >> c >> x0 >> n >> g; // 构造矩阵 Matrix M; M.m[0][0] = a % m; M.m[0][1] = c % m; M.m[1][0] = 0; M.m[1][1] = 1; // 计算 M^n Matrix Mn = pow_matrix(M, n, m); // 计算 X_n // X_n = (Mn[0][0] * X_0 + Mn[0][1]) mod m LL Xn = (mul(Mn.m[0][0], x0, m) + Mn.m[0][1]) % m; // 输出 X_n mod g cout << Xn % g << endl; return 0; }
数学推导(可选)
其实这个问题也可以用公式直接求解:
对于线性同余生成器: [ X_n = a^n X_0 + c \cdot \frac{a^n - 1}{a - 1} \pmod{m} \quad (a \neq 1) ]
但是:
- 需要特判 的情况
- 需要处理除法求逆元(要求 与 互质)
- 同样需要处理大数乘法溢出问题
矩阵方法更加通用,不需要特殊判断 的情况,也不需要 与 互质。
复杂度分析
- 快速乘:
- 矩阵乘法:常数次快速乘,
- 矩阵快速幂: 次矩阵乘法
总时间复杂度:,可以通过 的数据。
注意事项
- 模数问题:计算 时模数是 ,最后输出时模数是
- 输入输出:所有变量都可能达到 ,使用
long long - 快速乘:必须使用快速乘,否则会溢出
- 初始值: 可能为 0,需要正确处理
总结
这道题考察了:
- 将线性递推转化为矩阵乘法
- 矩阵快速幂的应用
- 处理大数乘法溢出的技巧(快速乘)
- 模运算的正确使用
- 1
信息
- ID
- 6138
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者