(扩展)Lucas定理——求大组合数取模

Lucas定理

P P P质数
C n r ≡ C ⌊ n / P ⌋ ⌊ r / P ⌋ × C n   m o d   P r   m o d   P   ( m o d   P ) C_n^r\equiv C_{\lfloor n/P\rfloor}^{\lfloor r/P\rfloor}\times C_{n\space mod\space P}^{r\space mod\space P}\space (mod \space P) CnrCn/Pr/P×Cn mod Pr mod P (mod P)
要用生成函数证明(不会,所以不证了)

实现直接递归:

void exgcd(long long a,long long b,long long &x,long long &y)
{
	if(b==0)
		x=1,y=0;
	else
	{
		long long x1,y1;
		exgcd(b,a%b,x1,y1);
		x=y1;
		y=x1-(a/b)*y1;
	}
}
long long inv(long long x,long long p)
{
	long long i,t;
	exgcd(x,p,i,t);
	i=(i%p+p)%p;
	return i;
}


long long C(long long n,long long r,long long MOD)
{
	if(n<r)
		return 0;
	if(r==0)
		return 1;
	long long ans=1LL;
	ans=C(n/MOD,r/MOD,MOD);
	n%=MOD,r%=MOD;
	long long a=1,b=1;
	for(int i=n;i>n-r;i--)
		a=(a*i)%MOD;
	for(int i=1;i<=r;i++)
		b=(b*i)%MOD;
	ans=(ans*a%MOD*inv(b,MOD))%MOD;
	return ans;
}

扩展Lucas定理

可以解决模数 P P P不是质数的组合数取模

P P P质因数分解: P = p 1 k 1 p 2 k 2 . . . p t k t P=p_1^{k_1}p_2^{k_2}...p_t^{k_t} P=p1k1p2k2...ptkt
如果我们求出一下方程中的 a 1 , a 2 . . . a t a_1,a_2...a_t a1,a2...at,就可以利用中国剩余定理求出 C n r C_n^r Cnr
{ C n r ≡ a 1   ( m o d   p 1 k 1 ) C n r ≡ a 2   ( m o d   p 2 k 2 ) C n r ≡ a 3   ( m o d   p 3 k 3 ) . . . C n r ≡ a t   ( m o d   p t k t ) \left \{ \begin{array}{c} C_n^r\equiv a_1\space (mod\space p_1^{k_1})\\ C_n^r\equiv a_2\space (mod\space p_2^{k_2})\\ C_n^r\equiv a_3\space (mod\space p_3^{k_3})\\ ...\\ C_n^r\equiv a_t\space (mod\space p_t^{k_t})\\ \end{array} \right . Cnra1 (mod p1k1)Cnra2 (mod p2k2)Cnra3 (mod p3k3)...Cnrat (mod ptkt)

现在来求 C n r ≡ a   ( m o d   p k ) C_n^r\equiv a\space(mod\space p^k) Cnra (mod pk)
C n r ≡ n ! ( n − r ) !   r !   ( m o d   p k ) C_n^r\equiv \frac {n!} {(n-r)!\space r!}\space (mod\space p^k) Cnr(nr)! r!n! (mod pk)
a ≡ n !   ( m o d   p k ) a\equiv n!\space(mod\space p^k) an! (mod pk) b ≡ r !   ( m o d   p k ) b\equiv r!\space(mod\space p^k) br! (mod pk) c ≡ ( n − r ) !   ( m o d   p k ) c\equiv(n-r)!\space(mod\space p^k) c(nr)! (mod pk)

将组合数分成三个阶乘计算: C n r ≡ a   b − 1 c − 1   ( m o d   p k ) C_n^r\equiv a\space b^{-1}c^{-1}\space(mod\space p^k) Cnra b1c1 (mod pk)
b − 1 b^{-1} b1表示逆元)
但此时如果 b b b p k p^k pk不互质,就无法求出逆元,所以需要把 a a a, b b b, c c c中的质因子 p p p全部提出来,求了逆元之后再乘回去。

举个例子:计算 19 !   ( m o d   3 2 ) 19! \space(mod\space 3^2) 19! (mod 32)
19 ! = 1 × 2 × 3 × 4 × . . . × 19 = ( 1 × 2 × 4 × 5 × 7 × 8 × 10 × 11 × 13 × 14 × 16 × 17 × 19 ) × 3 6 × ( 1 × 2 × 3 × 4 × 5 × 6 ) \begin{aligned} 19!&amp;=1\times 2\times 3\times 4\times ...\times 19\\ &amp;= (1\times 2\times 4\times 5\times 7\times 8\times 10\times 11\times 13\times 14\times 16\times 17\times 19)\times3^6 \times (1\times 2\times 3\times 4\times 5\times 6)\\ \end{aligned} 19!=1×2×3×4×...×19=(1×2×4×5×7×8×10×11×13×14×16×17×19)×36×(1×2×3×4×5×6)
即把阶乘中的 p p p倍数提出来,后面 6 ! 6! 6!继续递归求解;
第一个括号里的式子有周期性的:
1 × 2 × 4 × 5 × 7 × 8 ≡ 10 × 11 × 13 × 14 × 16 × 17   ( m o d   3 2 ) 1\times 2\times 4\times 5\times 7\times 8\equiv 10\times 11\times 13\times 14\times 16\times 17 \space (mod \space 3^2) 1×2×4×5×7×810×11×13×14×16×17 (mod 32)
所以暴力求出一个区间的值 t t t,结果乘 t ⌊ 19 9 ⌋ t^{\lfloor \frac {19} 9 \rfloor} t919

细节见代码:

long long pow_mod(long long a,long long b,long long p)
{
	long long res=1;
	while(b)
	{
		if(b&1LL)
			res=(res*a)%p;
		b>>=1LL;
		a=(a*a)%p;
	}
	return res;
}
void exgcd(long long a,long long b,long long &x,long long &y)
{
	if(b==0)
	{
		x=1,y=0;
		return;
	}
	exgcd(b,a%b,y,x);
	y-=(a/b)*x;
}
long long inv(long long a,long long p)//逆元必须用扩展欧几里得,因为p不为质数
{
	long long i,t;
	exgcd(a,p,i,t);
	i=(i%p+p)%p;
	return i;
}

long long Fac(long long N,long long p,long long pk)
{
	if(N==0)
		return 1;
	long long ans=1;
	if(N>=pk)//周期性
	{
		for(long long i=1;i<pk;i++)
			if(i%p)
				ans=(ans*i)%pk;
		ans=pow_mod(ans,N/pk,pk);
	}
	for(long long i=1;i<=N%pk;i++)//除了周期性的,剩下的几个数
		if(i%p)
			ans=(ans*i)%pk;
	ans=(ans*Fac(N/p,p,pk))%pk;//递归求解
	return ans;
}
long long C(long long N,long long R,long long p,long long pk)
{
	if(R==0)
		return 1;
	long long a=Fac(N,p,pk),b=Fac(N-R,p,pk),c=Fac(R,p,pk),k=0,ans;//求出几个阶乘,不包含质因子p
	//下面单独计算质因子p的个数
	for(long long i=N;i;i/=p)
		k+=i/p;
	for(long long i=N-R;i;i/=p)
		k-=i/p;
	for(long long i=R;i;i/=p)
		k-=i/p;
	ans=a*inv(b,pk)%pk*inv(c,pk)%pk*pow_mod(p,k,pk)%pk;
	return ans;
}
long long C(long long N,long long R,long long M)
{
	if(N<R)
		return 0;
	long long ans=0,m=M,temp;
	for(long long p=2,pk;p*p<=m;p++)
		if(m%p==0)//枚举模数M的因子p
		{
			pk=1;
			while(m%p==0)
				m/=p,pk*=p;
			temp=C(N,R,p,pk);//求C(N,R)mod p^k
			ans=(ans+temp*(M/pk)%M*inv(M/pk,pk)%M)%M;//中国剩余定理
		}
	if(m!=1)
	{
		temp=C(N,R,m,m);
		ans=(ans+temp*(M/m)%M*inv(M/m,m)%M)%M;
	}
	return ans;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值