• Lucas定理


    引入

    我们都知道,求解C(n,m)可以利用公式C(n,m)=C(n-1,m)+C(n-1,m-1)暴力打表求解。

    那么问题来了,求解C(n,m)%p(n和m是非负整数,p是素数),n和m都很大而p很小(注意:n和m很大是指相对很大,至少你存不下,p很小是指相对很小,一般为≤109左右),或者n,m不大但大于p。

    显然这样用阶乘解决不了。

    那么,我们就引入一个求解方法:Lucas定理


    结论

    首先,介绍Lucas定理的两个结论

    结论1:

      Lucas(n,m,p)=cm(n%p,m%p)×Lucas(n/p,m/p,p);

      Lucas(x,0,p)=1;

      其中:cm(a,b)=a!×(b!×(a-b)!)(p-2)%p;

             =(a!/(a-b)!)×(b!)(p-2)%p;

         你会发现这其实就是C(n,m)的逆元。

    结论2:

      把n写成p进制a[n]×a[n-1]×a[n-2]×…×a[0],把m写成p进制b[n]×b[n-1]×b[n-2]×…×b[0],则C(n,m)≡C(a[n],b[n])×C(a[n-1],b[n-1])×C(a[n-2],b[n-2])×…×C(a[0],b[0])(mod p)。


    Lucas定理的实现代码

    int cm(int n,int m)
    {
    	if(n<m)return 0;//注意n%p可能小于m%p,则直接返回0即可
    	if(2*m>n)m=n-m;
    	long long s1=1,s2=1;
    	for(int i=0;i<m;i++)
    	{
    		s1=s1*(n-i)%p;
    		s2=s2*(i+1)%p;
    	}
    	return s1*qpow(s2,p-2)%p;//求s2的p-2次方,不是用于求逆元,注意qpow是快速幂,不是内置函数
    }
    int lucas(int n,int m)
    {
    	if(!m)return 1;
    	return cm(n%p,m%p)*lucas(n/p,m/p)%p;
    }
    

    那么,可能会出现一个问题,如果需要大量的运用Lucas定理,建议打表。

    void pre_work()
    {
    	jc[0]=1;
    	for(int i=1;i<=n;i++)
    		jc[i]=jc[i-1]*i%p;
    	for(int i=0;i<=n;i++)
    		qsm[i]=qpow(jc[i],p-2)%p;
    }
    int cm(int x,int y)
    {
    	return jc[x]*qsm[y]*qsm[x-y];
    }
    int lucas(int x,int y)
    {
    	if(!y)return 1;
    	return cm(x%p,y%p)*lucas(x/p,y/p)%p;
    }
    

    rp++

  • 相关阅读:
    vim复制
    嵌入式Linux学习(二)
    (Java实现) 洛谷 P1042 乒乓球
    (Java实现) 洛谷 P1042 乒乓球
    (Java实现) 洛谷 P1071 潜伏者
    (Java实现) 洛谷 P1071 潜伏者
    (Java实现) 洛谷 P1025 数的划分
    (Java实现)洛谷 P1093 奖学金
    (Java实现)洛谷 P1093 奖学金
    Java实现 洛谷 P1064 金明的预算方案
  • 原文地址:https://www.cnblogs.com/wzc521/p/11112737.html
Copyright © 2020-2023  润新知