洛谷 P3746
给定整数
n, p, k, r,求:\[\left( \sum_{i = 0}^\infty C_{nk}^{ik + r} \right) \bmod p \]\(n \le 10^9, r < k \le 50\)
改写一些题目,就是 \(\sum\limits_{i \mod k = 0} C_{nk}^{i}\)。
令 \(f(i, j) = \sum\limits_{x \mod k = j \and x \le i} C_{i}^{x}\)。根据 \(C_{i}^j = C_{i - 1}^{j - 1} + C_{i - 1}^j\) 这个条件,可以得到 \(f(i, j) = f(i - 1, (j - 1) \! \mod k) + f(i - 1, j)\)
然后就可以进行矩阵乘法了。
时间复杂度:\(O(k^3 \log n)\)。
其实还能优化,因为转移是一个循环卷积的形式,把矩阵快速幂改改就能优化到 \(O(k^2 \log n)\)。
或者可以这么理解:因为转移矩阵是一个循环矩阵,做完快速幂后仍然是循环矩阵,只需要记一行即可,可以看成 \(f(i, j)\) 转移到 \(f(i', j + k)\) 的系数。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
ll n, f[55], ans[55], t[55];
int Mod, k, r;
void Mul(ll *a, ll *b) {
for (int i = 0; i < k; i++) {
for (int j = 0; j < k; j++) {
(t[(i + j) % k] += a[i] * b[j]) %= Mod;
}
}
for (int i = 0; i < k; i++) {
a[i] = t[i], t[i] = 0;
}
}
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> Mod >> k >> r;
if (k == 1) {
f[0] = 2;
} else {
f[0] = f[1] = 1;
}
ans[0] = 1;
for (n *= k; n; n >>= 1, Mul(f, f)) {
if (n & 1) Mul(ans, f);
}
cout << ans[r];
return 0;
}
浙公网安备 33010602011771号