#include<bits/stdc++.h> usingnamespace std; using ll = longlong;
// 题意概括:计算 (a^b) % m ll qmi_rec(ll a, ll b, ll m){ if (b == 0) return1 % m; // b=0时返回1,注意m=1的特殊情况 ll t = qmi_rec(a, b / 2, m); // 递归计算 a^(b/2) t = t * t % m; // 平方 if (b % 2 == 1) t = t * a % m; // 如果b是奇数,多乘一个a return t; }
intmain(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); ll a, b, m; cin >> a >> b >> m; cout << qmi_rec(a, b, m) << endl; return0; }
#include<bits/stdc++.h> usingnamespace std; using ll = longlong;
// 题意概括:计算 (a^b) % m ll qmi(ll a, ll b, ll m){ ll res = 1 % m; // 初始化结果,处理m=1的特殊情况 a %= m; // a可能大于m,先取模 while (b > 0) { if (b & 1) res = res * a % m; // 若b的当前末位为1,则累乘 a = a * a % m; // 底数平方 b >>= 1; // b右移一位,考察下一位 } return res; }
intmain(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); ll a, b, m; cin >> a >> b >> m; cout << qmi(a, b, m) << endl; return0; }
时间复杂度: 。循环次数等于 的二进制位数。
空间复杂度: 。只使用了常数个额外变量。
3.3 实现中的关键细节
取模运算 (Modulo Operation): 在计算 时,中间结果可能会非常大,超出标准整型甚至 long long 的表示范围。模运算的性质 是我们的护身符。为了防止溢出,每一次乘法运算后都必须立即进行取模。观察上面的代码,res = res * a % m 和 a = a * a % m 都遵循了这一原则。
数据类型 (Data Types): 即使 都在 int 范围内,中间乘积 a * a 或 res * a 仍可能溢出 int。例如,若 ,则 a*a 约为 ,超出了 int(约 )的范围。因此,在进行乘法运算时,相关变量(至少是 res 和 a)必须声明为 long long 类型,以确保中间结果能够被安全存储。using ll = long long; 是一个非常好的实践。
初始值: res 初始化为 而非简单的 。这是为了处理一个极端但有效的测试用例:。任何数对 取模都应得到 ,但如果 res 初始化为 ,在循环未执行时(如 )会错误地返回 。1 % m 能正确处理这种情况,当 时,结果为 。
3.4 O(log b) 乘法:快速乘
在进行模运算时,我们面临一个潜在的陷阱:即使 ,乘积 仍可能超出 long long 的表示范围(约 )。例如,当 都是 级别的数时,a * b 会溢出。为了解决这个问题,我们需要一种不直接计算 a * b 就能得到 的方法。
#include<bits/stdc++.h> usingnamespace std; using ll = longlong;
// 题意概括: 计算 (a * b) % m,防止 a * b 溢出 ll mul(ll a, ll b, ll m){ ll res = 0; a %= m; while (b > 0) { if (b & 1) res = (res + a) % m; // 乘法变加法 a = (a + a) % m; // 底数平方变底数加倍 b >>= 1; } return res; }
时间复杂度: 。
空间复杂度: 。
在 C++ 中,对于 64 位整数,我们还可以利用 __int128 类型来直接计算乘积,这在性能上通常优于 的快速乘。但快速乘的思想是独立于特定语言特性的,并且在处理更高精度或不同代数结构时,其原理依然适用。
1 2 3 4
// 使用 __int128 的版本,通常更快 ll mul_fast(ll a, ll b, ll m){ return (ll)((__int128)a * b % m); }
现在,我们可以将标准快速幂 qmi 函数中的 res * a % m 和 a * a % m 替换为 mul(res, a, m) 和 mul(a, a, m),从而写出一个能处理超大模数的快速幂版本。
// 矩阵乘法 Mat mul(Mat x, Mat y){ Mat res; for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { for (int k = 0; k < 2; k++) { res.mat[i][j] = (res.mat[i][j] + x.mat[i][k] * y.mat[k][j]) % m; } } } return res; }
// 矩阵快速幂 Mat qmi(Mat a, ll b){ Mat res; // 初始化为单位矩阵 res.mat[0][0] = res.mat[1][1] = 1;
while (b > 0) { if (b & 1) res = mul(res, a); a = mul(a, a); b >>= 1; } return res; }
Mat mul(Mat x, Mat y){ Mat res; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { for (int k = 0; k < 3; k++) { res.mat[i][j] = (res.mat[i][j] + x.mat[i][k] * y.mat[k][j]) % m; } } } return res; }
Mat qmi(Mat a, ll b){ Mat res; res.mat[0][0] = res.mat[1][1] = res.mat[2][2] = 1; // 单位矩阵 while (b > 0) { if (b & 1) res = mul(res, a); a = mul(a, a); b >>= 1; } return res; }
intmain(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); // 题意概括: 求 S_n = sum_{i=1 to n} F_i mod m cin >> n >> m;
if (n == 0) { cout << 0 << endl; return0; } if (n == 1) { cout << 1 % m << endl; return0; }
#include<bits/stdc++.h> usingnamespace std; using ll = longlong;
ll get_phi(ll n){ ll res = n; for (ll i = 2; i * i <= n; i++) { if (n % i == 0) { res = res / i * (i - 1); while (n % i == 0) n /= i; } } if (n > 1) res = res / n * (n - 1); return res; }
// flag用于标记b^c的真实值是否已经超过模数m bool flag; ll qmi_phi(ll a, ll b, ll m){ ll res = 1; flag = false; a %= m; while (b > 0) { if (b & 1) { if (res * a >= m) flag = true; res = res * a % m; } if (b > 1 && a * a >= m) flag = true; // 提前判断,防止a*a溢出 a = a * a % m; b >>= 1; } return res; }
ll qmi(ll a, ll b, ll m){ ll res = 1; a %= m; while (b > 0) { if (b & 1) res = res * a % m; a = a * a % m; b >>= 1; } return res; }
intmain(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); // 题意概括: 计算 a^(b^c) mod m ll a, b, c, m; cin >> a >> b >> c >> m;
#include<bits/stdc++.h> usingnamespace std; using ll = longlong;
// 题意概括: 给定 a, y, m, 求最小非负整数 b 使得 a^b = y (mod m) // 要求 gcd(a, m) == 1 ll bsgs(ll a, ll y, ll m){ if (y % m == 1 % m && m != 1) return0; // 特判 b=0 map<ll, int> hs; int s = sqrt(m) + 1;
// Baby steps ll t = y % m; for (int r = 0; r < s; ++r) { hs[t] = r; t = t * a % m; }
// a_S = a^s ll as = 1; for(int i = 0; i < s; ++i) as = as * a % m; if (as == 0) return-1; // a^s=0, 只有当y=0时有解,但y=0不互质
// Giant steps t = as; for (int q = 1; q <= s; ++q) { if (hs.count(t)) { ll res = (ll)q * s - hs[t]; if(res >= 0) return res; } t = t * as % m; } return-1; // No solution }
#include<bits/stdc++.h> usingnamespace std; using ll = longlong;
// 题意概括: 计算 (a^b) % p ll qmi(ll a, ll b, ll p){ ll res = 1 % p; a %= p; while (b > 0) { if (b & 1) res = res * a % p; a = a * a % p; b >>= 1; } return res; }
intmain(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); ll a, b, p; cin >> a >> b >> p; ll s = qmi(a, b, p); cout << a << "^" << b << " mod " << p << "=" << s << endl; return0; }
for (int i = 0; i < n1; i++) { for (int j = 0; j < n2; j++) { res[i + j] += (s1[i] - '0') * (s2[j] - '0'); } } for (int i = 0; i < n1 + n2 - 1; i++) { if (res[i] >= 10) { res[i + 1] += res[i] / 10; res[i] %= 10; } }
string ans; int len = n1 + n2 - 1; while (len > 0 && res[len] == 0) len--; for (int i = len; i >= 0; i--) ans += to_string(res[i]); // 截断,只保留末尾L位 if (ans.size() > L) ans = ans.substr(ans.size() - L); return ans; }
// 高精度快速幂 string qmi(string a, int b){ string res = "1"; while (b > 0) { if (b & 1) res = mul(res, a); a = mul(a, a); b >>= 1; } return res; }