1 条题解
-
0
题目大意
给定整数 、 和 ,其中 是质数。对于每个 ,计算在模 的有限域 上,所有 矩阵中,秩恰好等于 的矩阵个数。由于结果可能很大,要求输出对 取模后的答案。
数据范围:,,。
前置知识与公式推导
在大小为 的有限域 上(本题中 ),秩为 的 矩阵个数由以下著名公式给出:
$$N(n, r, q) = \left( \prod_{i=0}^{r-1} (q^n - q^i) \right)^2 \cdot \frac{1}{\prod_{i=0}^{r-1} (q^r - q^i)} $$该公式可以进一步改写为递推形式,便于在 时间内求出所有 的答案。
我们记 ,并令 。则有:
- 当 时,只有全零矩阵满足条件,故 。
- 当 时,秩不可能超过 ,故 。
- 当 时,满足递推关系:
推导要点:比较 与 的表达式中相同部分的比值,化简即得上述递推式。注意分母 在模意义下可逆,除非 。本题中由于 且 是质数,可以证明在题目约束下分母恒不为零(除非 但 且 , 的阶通常大于 ),因此可直接求逆元。
算法实现细节
由于 可达 ,而 最多只有 ,我们需要在 或 时间内完成计算。
关键步骤:
-
预处理 的幂次及其逆元
计算 以及对应的逆元 。这一步只需一次线性递推,时间复杂度 。 -
预处理
对每个 ,计算 ,并用快速幂求其在模 下的逆元。这一步复杂度 ,可接受。 -
计算
使用快速幂在 时间内求出。 -
递推计算
递推式中的各项均可通过已预处理的数组 得到:- 分母逆元 已预处理
因此 $A_r = A_{r-1} \cdot a \cdot b \cdot b \cdot inv\_c[r] \pmod{MOD}$。注意乘法过程中每一步都立即取模,防止
long long溢出。 -
边界处理
若 ,则直接令 。
复杂度分析
- 预处理 ,其中 。
- 递推 。
- 总时间复杂度 ,在 时完全可行。
- 空间复杂度 。
AC 代码
#include <iostream> #include <vector> #include <algorithm> using namespace std; typedef long long ll; const int MOD = 998244353; // 快速幂:计算 a^b mod mod ll qpow(ll a, ll b, ll mod) { ll res = 1; a %= mod; while (b > 0) { if (b & 1) res = res * a % mod; a = a * a % mod; b >>= 1; } return res; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); ll n, p; int k; cin >> n >> p >> k; ll q = p % MOD; ll max_r = min(n, (ll)k); // 秩超过n时答案必为0 // 1. 预处理 q^0 ~ q^k mod MOD vector<ll> pow_q(k + 1); pow_q[0] = 1; for (int i = 1; i <= k; ++i) { pow_q[i] = pow_q[i-1] * q % MOD; } // 2. 预处理 q^{-0} ~ q^{-k} mod MOD ll inv_q = qpow(q, MOD - 2, MOD); vector<ll> inv_pow_q(k + 1); inv_pow_q[0] = 1; for (int i = 1; i <= k; ++i) { inv_pow_q[i] = inv_pow_q[i-1] * inv_q % MOD; } // 3. 预处理 (q^r - 1) 的逆元 vector<ll> inv_c(k + 1); for (int r = 1; r <= k; ++r) { ll c = (pow_q[r] - 1 + MOD) % MOD; inv_c[r] = qpow(c, MOD - 2, MOD); } // 4. 计算 q^n mod MOD ll qn = qpow(q, n, MOD); // 5. 递推计算答案 vector<ll> ans(k + 1); ans[0] = 1; // 秩0只有零矩阵 for (int r = 1; r <= k; ++r) { if (r > max_r) { ans[r] = 0; continue; } ll a = pow_q[r-1]; // q^{r-1} ll term = qn * inv_pow_q[r-1] % MOD; // q^{n - r + 1} ll b = (term - 1 + MOD) % MOD; // q^{n-r+1} - 1 ans[r] = ans[r-1] * a % MOD; ans[r] = ans[r] * b % MOD * b % MOD; ans[r] = ans[r] * inv_c[r] % MOD; } // 6. 输出 if (k >= 0) { printf("%lld", ans[0]); for (int i = 1; i <= k; ++i) { printf(" %lld", ans[i]); } printf("\n"); } return 0; }
- 1
信息
- ID
- 6510
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者