• 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++

  • 相关阅读:
    斐波那契数列 详解
    ASP.NET 系列:RBAC权限设计
    架构系列:ASP.NET 项目结构搭建
    EntityFramework系列:Repository模式与单元测试
    PHP 系列:PHP Web 开发基础
    Java Web系列:Spring Boot 基础
    ddddddd
    ddd
    asdfsf
    sdfsdfsdf
  • 原文地址:https://www.cnblogs.com/wzc521/p/11112737.html
Copyright © 2020-2023  润新知