【模板】扩展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} \rfloor$个,将$p$提出来,这$\lfloor \frac{n}{p} \rfloor$个数又成为一个阶乘的形式,递归即可,总层数不会超过$log$。这部分的答案就是$p^{\lfloor \frac{n}{p} \rfloor} * Fac(\lfloor \frac{n}{p} \rfloor)$。
  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\n", Lucas(n, m, P));

  return 0;
}

原文地址:https://www.cnblogs.com/Dance-Of-Faith/p/9360083.html

时间: 2024-11-09 00:17:15

【模板】扩展Lucas随想的相关文章

CF.100633J.Ceizenpok&#39;s formula(扩展Lucas)

题目链接 ->扩展Lucas //求C_n^k%m #include <cstdio> typedef long long LL; LL FP(LL x,LL k,LL p) { LL t=1ll; for(; k; k>>=1,x=x*x%p) if(k&1) t=t*x%p; return t; } void Exgcd(LL a,LL b,LL &x,LL &y) { if(!b) x=1ll, y=0ll; else Exgcd(b,a%b,y

BZOJ.2142.礼物(扩展Lucas)

题目链接 答案就是C(n,m1) * C(n-m1,m2) * C(n-m1-m2,m3)...(mod p) 使用扩展Lucas求解. 一个很简单的优化就是把pi,pi^ki次方存下来,因为每次分解p都是很慢的. 注意最后p不为1要把p再存下来!(质数) COGS 洛谷上的大神写得快到飞起啊QAQ 就这样吧 //836kb 288ms #include <cmath> #include <cstdio> typedef long long LL; int cnt,P[500],P

[BZOJ2142]礼物(扩展Lucas)

2142: 礼物 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 2286  Solved: 1009[Submit][Status][Discuss] Description 一年一度的圣诞节快要来到了.每年的圣诞节小E都会收到许多礼物,当然他也会送出许多礼物.不同的人物在小E 心目中的重要性不同,在小E心中分量越重的人,收到的礼物会越多.小E从商店中购买了n件礼物,打算送给m个人 ,其中送给第i个人礼物数量为wi.请你帮忙计算出送礼物的方案数(

BZOJ2142 礼物 【扩展Lucas】

题目 一年一度的圣诞节快要来到了.每年的圣诞节小E都会收到许多礼物,当然他也会送出许多礼物.不同的人物在小E 心目中的重要性不同,在小E心中分量越重的人,收到的礼物会越多.小E从商店中购买了n件礼物,打算送给m个人 ,其中送给第i个人礼物数量为wi.请你帮忙计算出送礼物的方案数(两个方案被认为是不同的,当且仅当存在某 个人在这两种方案中收到的礼物不同).由于方案数可能会很大,你只需要输出模P后的结果. 输入格式 输入的第一行包含一个正整数P,表示模: 第二行包含两个整整数n和m,分别表示小E从商

BZOJ3129 [Sdoi2013]方程 【扩展Lucas】

题目 给定方程 X1+X2+. +Xn=M 我们对第l..N1个变量进行一些限制: Xl < = A X2 < = A2 Xn1 < = An1 我们对第n1 + 1..n1+n2个变量进行一些限制: Xn1+l > = An1+1 Xn1+2 > = An1+2 Xnl+n2 > = Anl+n2 求:在满足这些限制的前提下,该方程正整数解的个数. 答案可能很大,请输出对p取模后的答案,也即答案除以p的余数. 输入格式 输入含有多组数据,第一行两个正整数T,p.T表示

模板—数学—Lucas

模板—数学—Lucas Code: #include <cstdio> #include <algorithm> using namespace std; #define N 100010 int n,m,p,inv[N],powq[N]; int lucas(int n,int m) { if(n<m) return 0; if(n<=p&&m<=p) return 1ll*powq[n]*inv[m]%p*inv[n-m]%p; return

VS自定义项目模板:[2]创建VSIX项目模板扩展

VS2013(VS2010等版本也适用,均需安装Visual Studio SDK) 如何创建VSIX扩展项目? 1 新建项目-->选择扩展性中的VSIX Project项目. 2 双击打开source.extension.vsixmanifest文件,设置VSIX扩展项目的一些基础信息. 3 为VSIX扩展项目添加上篇经验导出的模板. 在Assets(资产)标签中新建,选择Type为Microsoft.VisualStudio.ProjectTemplate(项目模板),Source选择Fil

tornado之模板扩展

当我们有多个模板的时候,很多模板之间其实相似度很高.我们期望可以重用部分网页代码.这在tornado中可以通过extends语句来实现.为了扩展一个已经存在的模板,你只需要在新的模板文件的顶部放上一句{% extends "filename.html" %}.比如,为了在新模板中扩展一个父模板(在这里假设为之前我们使用过的index.html),你可以这样使用:{% extends "index.html" %} 这就使得新文件继承index.html的所有标签,并

第三章:模板扩展

在第二章中,我们看到了Tornado模板系统如何简单地传递信息给网页,使你在插入动态数据时保持网页标记的整洁.然而,大多数站点希望复用像header.footer和布局网格这样的内容.在这一章中,我们将看到如何使用扩展Tornado模板或UI模块完成这一工作. 3.1 块和替换 当你花时间为你的Web应用建立和制定模板时,希望像你的后端Python代码一样重用你的前端代码似乎只是合逻辑的,不是吗?幸运的是,Tornado可以让你做到这一点.Tornado通过extends和block语句支持模板