洛谷 P3746

洛谷 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;
}
posted @ 2026-01-09 22:35  xiehanrui0817  阅读(21)  评论(0)    收藏  举报