[解题报告] [省选联考 2020 A 卷] 组合数问题

传送门 🚪

题意

\[\left( \sum_{k = 0}^{n} f(k) \times x^k \times \binom{n}{k} \right) \bmod p \]

其中 \(f(k)\) 为关于 \(k\)\(m\) 次多项式.

\(n, m, x, p\) 给定, \(n \le 10^9, m \le 10^3\).

思路

前置知识

  1. 二项式定理

    \[(x + y)^n = \sum_{i = 0}^{n} x^i y^{n - i} \]

  2. 第二类斯特林数

    \[n^m = \sum_{i = 0}^{m} \binom{n}{i} \times i! \times \begin{Bmatrix}m\\i\end{Bmatrix} \]

    组合意义证明:
    \(n^m\) 为将 \(m\) 个不同的球放进 \(n\) 个不同的盒子且允许空盒的方案. 右边的式子相当于枚举非空盒子的数量, 然后从 \(n\) 个盒子中取出 \(i\) 个盒子, 然后乘上 \(m\) 个不同的球放入 \(i\) 个盒子且无空盒的方案数, 由于第二类斯特林数中的盒子是相同的, 所以还要乘上 \(i!\).

  3. 下降幂

    \[x^{\underline{i}} = \prod_{j = x - i + 1}^{x} j \]

    所以上述的第二类斯特林数的性质也可表示为

    \[n^m = \sum_{i = 0}^{m} n^{\underline{i}} \times \begin{Bmatrix}m\\i\end{Bmatrix} \]

    • 性质

      \[x^{\underline{i}} \times \binom{n}{x} = n^{\underline{i}} \times \binom{n - i}{x - i} \]

推导过程

看了洛谷题解的第一面, 发现有三种不同的写法.

方法一 组合数暴推

\[\begin{aligned} \sum_{k = 0}^{n} f(k) \times x^k \times \binom{n}{k} &= \sum_{k = 0}^{n} \sum_{i = 0}^{m} a_i k^i \times x^k \times \binom{n}{k} \\ &= \sum_{k = 0}^{n} \sum_{i = 0}^{m} a_i \sum_{j = 0}^{i} \binom{k}{j} \times j! \times \begin{Bmatrix}i\\j\end{Bmatrix} \times x^k \times \binom{n}{k} \\ 由于\;m\;比较小, 所以把和\;m\;有关的项提前. \\ &= \sum_{i = 0}^{m} a_i \sum_{j = 0}^{i} j! \times \begin{Bmatrix}i\\j\end{Bmatrix} \sum_{k = 0}^{n} \binom{k}{j} \times \binom{n}{k} \times x^k \\ &= \sum_{i = 0}^{m} a_i \sum_{j = 0}^{i} j! \times \begin{Bmatrix}i\\j\end{Bmatrix} \times \binom{n}{j} \sum_{k = 0}^{n} \binom{n - j}{k - j} \times x^k \\ 令\;h = k - j\;, 则\;k = h + j\;. \\ &= \sum_{i = 0}^{m} a_i \sum_{j = 0}^{i} j! \times \begin{Bmatrix}i\\j\end{Bmatrix} \times \binom{n}{j} \sum_{h = 0}^{n - j} \binom{n - j}{h} \times x^{h + j} \\ &= \sum_{i = 0}^{m} a_i \sum_{j = 0}^{i} j! \times \begin{Bmatrix}i\\j\end{Bmatrix} \times \binom{n}{j} \times x^j \sum_{h = 0}^{n - j} \binom{n - j}{h} \times x^h \\ &= \sum_{i = 0}^{m} a_i \sum_{j = 0}^{i} j! \times \begin{Bmatrix}i\\j\end{Bmatrix} \times \binom{n}{j} \times x^j \times (x + 1)^{n - j} \\ \end{aligned} \]

\(O(m^2)\) 预处理第二类斯特林数,再 \(O(m^2)\) 求解即可。

方法二 结合下降幂

由「前置知识」中下降幂的性质中我们可以发现,下降幂和组合数有着比较紧密的联系,所以我们考虑引入下降幂。

先把普通多项式转化为下降幂多项式,即

\[\sum_{i = 0}^{m} b_i \times k^{\underline{i}} = \sum_{i = 0}^{n} a_i \times k^i \]

假设我们已经求出 \(b_i\) (方法会在后面给出),那么尝试把 \(b_i\) 带入原式。

\[\begin{aligned} \sum_{k = 0}^{n} f(k) \times x^k \times \binom{n}{k} &= \sum_{k = 0}^{n} \sum_{i = 0}^{m} b_i \times k^{\underline{i}} \times x^k \times \binom{n}{k} \\ 根据下降幂的性质, 可得 \\ &= \sum_{k = 0}^{n} \sum_{i = 0}^{m} b_i \times x^k \times n^{\underline{i}} \times \binom{n - i}{k - i} \\ &= \sum_{i = 0}^{m} b_i \times x^i \times n^{\underline{i}} \sum_{k = 0}^{n} \binom{n - i}{k - i} \times x^{k - i} \\ &= \sum_{i = 0}^{m} b_i \times x^i \times n^{\underline{i}} \times (x + 1)^{n - i} \\ \end{aligned} \]

可以 \(O(m)\) 求解。

接下来考虑如何求 \(b_i\)

把式子列出来。

\[\sum_{i = 0}^{m} b_i \times k^{\underline{i}} = \sum_{i = 0}^{m} a_i \times k^i \]

根据第二类斯特林数的性质,把 \(k^i\) 替换掉。

\[\begin{aligned} \sum_{i = 0}^{m} b_i \times k^{\underline{i}} &= \sum_{i = 0}^{m} a_i \sum_{j = 0}^{i} \binom{k}{j} \times j! \times \begin{Bmatrix}i\\j\end{Bmatrix} \\ &= \sum_{i = 0}^{m} a_i \sum_{j = 0}^{i} k^{\underline{j}} \times \begin{Bmatrix}i\\j\end{Bmatrix} \\ &= \sum_{i = 0}^{m} k^{\underline{i}} \sum_{j = i}^{m} \begin{Bmatrix}j\\i\end{Bmatrix} \times a_j \end{aligned} \]

所以,

\[b_i = \sum_{j = i}^{m} \begin{Bmatrix}j\\i\end{Bmatrix} \times a_j \]

\(O(m^2)\) 暴力预处理第二类斯特林数即可。

方法三 生成函数

生成函数基本上没学过,还没看懂,到时候再补。

代码

用的是方法二,结合下降幂。

#include <iostream>

using namespace std;

typedef long long ll;

const int _ = 1e3 + 7;

int n, m, x, mod, a[_], b[_], S[_][_], fpn[_], pwx[_], pwxp[_];

int Pw(int a, int p) {
  int res = 1;
  while (p) {
    if (p & 1) res = (ll)res * a % mod;
    a = (ll)a * a % mod;
    p >>= 1;
  }
  return res;
}

void Init() {
  cin >> n >> x >> mod >> m;
  for (int i = 0; i <= m; ++i) cin >> a[i];
  fpn[0] = pwx[0] = 1;
  for (int i = 1; i <= m; ++i) fpn[i] = (ll)fpn[i - 1] * (n - i + 1) % mod, pwx[i] = (ll)pwx[i - 1] * x % mod;
  pwxp[m] = Pw(x + 1, n - m);
  for (int i = m - 1; i >= 0; --i) pwxp[i] = (ll)pwxp[i + 1] * (x + 1) % mod;
  S[0][0] = 1;
  for (int i = 1; i <= m; ++i)
    for (int j = 1; j <= i; ++j)
      S[i][j] = ((ll)S[i - 1][j] * j % mod + S[i - 1][j - 1]) % mod;
  for (int i = 0; i <= m; ++i)
    for (int j = i; j <= m; ++j)
      b[i] = (b[i] + (ll)S[j][i] * a[j] % mod) % mod;
}

void Run() {
  int ans = 0;
  for (int i = 0; i <= m; ++i)
    ans = (ans + (ll)b[i] * fpn[i] % mod * pwx[i] % mod * pwxp[i] % mod) % mod;
  cout << ans << endl;
}

int main() {
  Init();
  Run();
  return 0;
}
posted @ 2020-10-15 10:48  BruceW  阅读(101)  评论(0编辑  收藏  举报