zoukankan      html  css  js  c++  java
  • 总结「卢卡斯定理」

    搬运自远古的洛咕博客,故文风与现在有很大不同


    Lucas 卢卡斯定理

    (p) 是质数,则对于任意整数 (1 leq m leq n) 有:

    [C_n^m equiv C_{n { m {mod}} p}^{m { m {mod}} p} imes C_{n/p}^{m/p} ({ m {mod}} p) ]

    证明比较复杂,没怎么听懂。

    证明过程中有个结论可能会有用:

    (p) 是质数,将 (a,b) 转化为 (p) 进制:

    [egin{aligned} a =a_k imes p^k +a_{k-1} imes p^{k-1}+...+a_1 imes p+ a_0\ b =b_k imes p^k +b_{k-1} imes p^{k-1}+...+b_1 imes p+ b_0\ end{aligned} ]

    则:

    [C_a^b equiv C_{a_k}^{b_k} imes C_{a_{k-1}}^{b_{k-1}} imes ... imes C_{a_0}^{b_0} ({ m {mod}} p) ]


    模版:

    P3807 【模板】卢卡斯定理

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define lxl long long
    using namespace std;
    
    inline lxl pow(lxl a,lxl b,lxl p)
    {
    	lxl ans=1%p;
    	while(b>0)
    	{
    		if(b&1) ans=(ans*a)%p;
    		a=(a*a)%p;
    		b>>=1;
    	}
    	return ans%p;
    }
    
    inline lxl mul(lxl a,lxl b,lxl p)
    {
    	lxl ans=0;
    	while(b>0)
    	{
    		if(b&1) ans=(ans+a)%p;
    		a=(a+a)%p;
    		b>>=1;
    	}
    	return ans%p;
    }
    
    
    inline lxl inv(lxl x,lxl p)
    {
    	return pow(x,p-2,p);
    }
    
    inline lxl C(lxl n,lxl m,lxl p)
    {
    	if(n<m) return 0;
    	lxl up=1,down=1;
    	for(lxl i=n-m+1;i<=n;i++) up=mul(up,i,p);
    	for(lxl i=1;i<=m;i++) down=mul(down,i,p);
    	return mul(up,inv(down,p),p);
    }
    
    inline lxl Lucas(lxl n,lxl m,lxl p)
    {
    	return m ? C(n%p,m%p,p)*Lucas(n/p,m/p,p)%p :1;
    }
    
    int main()
    {
    	//freopen("P3807.in","r",stdin);
    	int t;scanf("%d",&t);
    	lxl n,m,p;
    	while(t--)
    	{
    		scanf("%lld%lld%lld",&n,&m,&p);
    		printf("%lld
    ",Lucas(n+m,n,p));
    	}
    	return 0;
    }
    
    

    exLucas 扩展卢卡斯定理

    说是扩展卢卡斯定理,但是好像和Lucas关系不大。

    Lucas定理中,要求 (p) 必须为质数,那么 (p) 是任意数时怎么求呢?这里用到扩展Lucas。

    (ans=C_n^m { m {mod}} p)

    (p) 分解质因数:

    [p=p_1^{k_1} imes p_2^{k_2} imes ... imes p_x^{k_x} ]

    则有:

    [egin{cases} ans equiv c_1 ({ m {mod}} p_1^{k_1} ) \ ans equiv c_2 ({ m {mod}} p_2^{k_2} ) \ ... \ ans equiv c_x ({ m {mod}} p_x^{k_x} ) end{cases} ]

    其中 (c_i=C_n^m { m {mod}} p_i^{k_i})

    也就是说,求解 (c_i) 后,再用CRT合并同余方程组即可求出 (ans)

    如何求解 (c_i)

    [c_i=frac{n!}{m!(n-m)!} { m {mod}} p_i^{k_i} ]

    注意到 (m!,(n-m)!)(p_i^{k_i}) 不一定互素,不能直接求解逆元。

    考虑将 (n!,m!,(n-m)!)(p_i) 因子除去,使其与 (p_i^{k_i}) 互素:

    [frac{frac{n!}{p_i^x}}{frac{m!}{p_i^y} frac{(n-m)!}{p_i^z}} imes p_i^{x-y-z} { m {mod}} p_i^{k_i} ]

    其中 (x,y,z) 分别为 (n!,m!,(n-m)!)(p_i) 因子的个数。

    此时即可用逆元求解。


    如何求解 (frac{n!}{p_i^x})

    (n!) 进行变形:

    [egin{aligned} n!&=1 imes 2 imes 3 imes ... imes n\ &=(p_i imes 2p_i imes ...)prod_{i=1,i ot equiv 0 { m {mod}} p_i} ^{n} i\ &=p_i^{lfloor frac {n}{p_i} floor} imes {lfloor frac {n}{p_i} floor}! imes prod_{i=1,i ot equiv 0 { m {mod}} p_i} ^{n} i end{aligned} ]

    可以发现其中 (prod_{i=1,i ot equiv 0 { m {mod}} p_i} ^{n} i) 是有循环节的,可以暴力求,例如:

    [egin{aligned} 22! &equiv (1⋅2⋅4⋅5⋅7⋅8) imes (10⋅11⋅13⋅14⋅16⋅17) imes (19⋅20⋅22) imes (3⋅6⋅9⋅12⋅15⋅18⋅21)pmod {3^2}\ &equiv(1cdot 2cdot 4cdot 5cdot 7cdot 8)^2 imes (19cdot 20cdot 22) imes 3^7 imes (1cdot 2cdot 3cdot 4cdot 5cdot 6cdot 7)pmod {3^2}\ &equiv 3^7 imes 7! imes (1cdot 2cdot 4cdot 5cdot 7cdot 8)^2 imes (19cdot 20cdot 22)pmod{3^2}\ end{aligned} ]

    所以:

    [=p_i^{lfloor frac {n}{p_i} floor} imes {lfloor frac {n}{p_i} floor}! imes (prod_{i=1,i ot equiv 0 { m {mod}} p_i} ^{p_i^{k_i}} i)^{lfloor frac {n}{p_i^{p_k}} floor} imes prod_{i=p_i^{k_i} imes lfloor frac {n}{p_i^{k_i}} floor,i ot equiv 0 { m {mod}} p_i} ^{n} i ]

    发现其中 ({lfloor frac {n}{p_i} floor}!)(n!) 的求解方法是一样的,递归求解即可。

    部分参考:Fading大佬的博客


    模版:

    P4720 【模板】扩展卢卡斯

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #define lxl long long
    using namespace std;
    
    inline lxl read()
    {
    	lxl x=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    	return x*f;
    }
    
    inline lxl fmi(lxl a,lxl b,lxl p)
    {
    	lxl ans=1;
    	while(b>0)
    	{
    		if(b&1) ans=(ans*a)%p;
    		a=(a*a)%p;
    		b>>=1;
    	}
    	return ans;
    }
    
    inline lxl exgcd(lxl a,lxl b,lxl &x,lxl &y)
    {
    	if(!b) {x=1,y=0;return a;}
    	lxl k=exgcd(b,a%b,x,y);
    	lxl z=x;x=y,y=z-a/b*y;
    	return k;
    }
    
    inline lxl inv(lxl a,lxl b)
    {
    	lxl x,y;
    	lxl g=exgcd(a,b,x,y);
    	return g==1?(x+b)%b:-1;
    }
    
    inline lxl mul(lxl n,lxl pi,lxl pk)
    {
    	if(!n) return 1;
    	lxl ans=1;
    	if(n/pk)
    	{
    		for(lxl i=1;i<=pk;i++)
    			if(i%pi) ans=ans*i%pk;
    		ans=fmi(ans,n/pk,pk);
    	}
    	for(lxl i=2;i<=n%pk;i++)
    		if(i%pi) ans=ans*i%pk;
    	return ans*mul(n/pi,pi,pk)%pk;
    }
    
    inline lxl C(lxl n,lxl m,lxl p,lxl pi,lxl pk)
    {
    	if(n<m) return 0;
    	lxl a=mul(n,pi,pk),b=mul(m,pi,pk),c=mul(n-m,pi,pk),k=0,ans;
    	for(lxl i=n;i;i/=pi) k+=i/pi;
    	for(lxl i=m;i;i/=pi) k-=i/pi;
    	for(lxl i=n-m;i;i/=pi) k-=i/pi;
    	ans=a*inv(b,pk)%pk*inv(c,pk)%pk*fmi(pi,k,pk)%pk;
    	ans=ans*(p/pk)%p*inv(p/pk,pk)%p;
    	return ans;
    }
    
    inline lxl exLucas(lxl n,lxl m,lxl p)
    {
    	lxl ans=0,x=p,t=sqrt(p);
    	for(lxl i=2;i<=t;i++)
    		if(x%i==0)
    		{
    			lxl pk=1;
    			while(x%i==0) x/=i,pk=pk*i%p;
    			ans=(ans+C(n,m,p,i,pk))%p;
    		}
    	if(x>1) ans=(ans+C(n,m,p,x,x))%p;
    	return ans;
    }
    
    lxl n,m,p;
    
    int main()
    {
    	//freopen("P4720.in","r",stdin);
    	n=read(),m=read(),p=read();
    	printf("%lld
    ",exLucas(n,m,p));
    	return 0;
    }
    
  • 相关阅读:
    selenium之css选择器高级用法
    常系数线性齐次递推新理解
    关于莫队本质的理解
    2021.5.8总结
    决策单调性优化dp
    字符串 复习
    5.1总结
    树分块 学习笔记
    莫反 复习
    P4570 [BJWC2011]元素
  • 原文地址:https://www.cnblogs.com/syc233/p/13885167.html
Copyright © 2011-2022 走看看