模板扩展Lucas随想

Posted dance-of-faith

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了模板扩展Lucas随想相关的知识,希望对你有一定的参考价值。

扩展Lucas解决的还是一个很Simple的问题:

求:$C_{n}^{m} ; mod ; p$。

其中$n,m$都会比较大,而$p$不是很大,而且不一定是质数。

扩展Lucas可以说和Lucas本身并没有什么关系,重要的是中国剩余定理。扩展Lucas这个算法中教会我们的除了算组合数,还有在模数不是质数的时候,往往可以用$CRT$来合并答案。

将原模数质因数分解:$P = prod_{i = 1}^{m} p_{i}^{k_{i}}$。

列出$m$个同余方程,第$i$个形如:$C_{n}^{m} ; equiv a_{i} (mod ; p_{i}^{k_{i}})$。

由于$m$个方程中模数互质,则$CRT$后就是原答案。

现在来对于某个方程求解$a_{i}$是多少,即$C_{n}^{m} ; mod ; p^{k}$的答案。

把组合数转化成阶乘:$frac{n!}{m!(n - m)!}$,我们先求一个阶乘在$mod ; p^{k}$下的值,设这个函数为$Fac(n)$。

常规的对于式子下方的阶乘我们需要求逆元,而阶乘中存在$p$的倍数,这意味可能不与$p^{k}$互质。为了解决这个问题,我们将有关$p$单独考虑,于是一个算阶乘的函数将包括两部分:

  1. 首先考虑所有$p$的倍数,总共有$lfloor frac{n}{p} floor$个,将$p$提出来,这$lfloor frac{n}{p} floor$个数又成为一个阶乘的形式,递归即可,总层数不会超过$log$。这部分的答案就是$p^{lfloor frac{n}{p} floor} * Fac(lfloor frac{n}{p} floor)$。
  2. 剩下的数都将与$p^{k}$互质。我们考虑以$p^{k}$分块,我们可以证明每段$p^{k}$中所有不是$p$的倍数的数的乘积在模$p^{k}$意义下是相同的。具体原因在于$i + p^{k} equiv i (mod ; p^{k})$。通过暴力计算,这部分的复杂度就是$O(p^{k})$的。

接下来就没有什么问题了,用扩展欧几里得求逆元,有关$p$的幂次在除法时指数相减就行了。

 

技术分享图片
#include <cstdio>

typedef long long LL;

int P;

int Pow(int x, LL b, int p) {
  static int re;
  for (re = 1; b; b >>= 1, x = (LL) x * x % p)
    if (b & 1) re = (LL) re * x % p;
  return re;
}
int Ex_gcd(int a, int b, int &x, int &y) {
  if (b == 0) return x = 1, y = 0, a;
  int gcd = Ex_gcd(b, a % b, y, x);
  y -= a / b * x;
  return gcd;
}
int Inv(int a, int p) {
  static int x, y;
  int gcd = Ex_gcd(a, p, x, y);
  if (gcd != 1) throw;
  return (x % p + p) % p;
}

int Fac(LL n, int p, int pk) {
  if (n == 0) return 1;
  int re = 1;
  for (int i = 1; i <= pk; ++i)
    if (i % p != 0) re = (LL) re * i % pk;
  re = Pow(re, n / pk, pk);
  for (int i = 1; i <= n % pk; ++i)
    if (i % p != 0) re = (LL) re * i % pk;
  return (LL) re * Fac(n / p, p, pk) % pk;
}

int Crt(LL n, LL m, int p, int pk) {
  int fn = Fac(n, p, pk), fm = Fac(m, p, pk), fnm = Fac(n - m, p, pk);
  int cp = 0;
  for (LL i = n; i; i /= p) cp += i / p;
  for (LL i = m; i; i /= p) cp -= i / p;
  for (LL i = n - m; i; i /= p) cp -= i / p;
  int a = (LL) fn * Inv(fm, pk) % pk * Inv(fnm, pk) % pk * Pow(p, cp, pk) % pk;
  return (LL) a * (P / pk) % P * Inv(P / pk, pk) % P;
}

int Lucas(LL n, LL m, int p) {
  int re = 0, x = p;
  for (int i = 2; i <= p; ++i) {
    if (x % i != 0) continue;
    int pk = 1;
    while (x % i == 0) pk *= i, x /= i;
    re = (re + Crt(n, m, i, pk)) % p;
  }
  return re;
}

int main() {
  LL n, m;
  scanf("%lld%lld%d", &n, &m, &P);
  printf("%d
", Lucas(n, m, P));

  return 0;
}
View Code

 

以上是关于模板扩展Lucas随想的主要内容,如果未能解决你的问题,请参考以下文章

组合数模板 - Lucas

luogu P3807 卢卡斯定理 模板

如何在扩展另一个文件的 django 模板中使用带有动态内容的 html 块片段?

Lucas定理求组合数模板

摘自队友devil的lucas模板

模板—数学—Lucas