zoukankan      html  css  js  c++  java
  • Lucas 定理

    Lucas定理

    内容

    [C^{n}_{m}equiv C^{lfloor frac{n}{p} floor}_{lfloor frac{m}{p} floor} imes C^{n\%p}_{m\%p}quad(mod;p) ]

    条件是 (p) 为质数。

    (Lucas) 定理的主要作用是当组合数过大,无法处理其中 (n)(m) 的阶乘。通过展开将其表示成累积形式。

    复杂度为 (O(log^n))

    注意,由于实际上是将问题转移到 (p) 身上,所以 (Lucas) 的局限是 (p) 不能太大。

    证明

    • 首先提出一个引理:(pmid C^{i}_{p}quad(p=1,2,3,cdots,p-1))

      证明如下:

      [f 原式等于:scr frac{overbrace{p(p-1)cdots (p-i+1)}^{i}}{i!} ]

      易知一个数 (x) 在长度为 (x) 的连续自然数中必有一个整倍数。

      那么可以知道下面的 (i) 一定对应上面的某数。

      但是可以知道,小于 (p) 的数中只有 (1) 可以整除它,那么我们就可以知道 (1) 对应 (p) ,在除完后 (p) 这个因子被保留了下来,当然可以被 (p) 整除。

    下面正式开始证明。

    利用了二项式展开,已知:

    [(1+x)^{np+m}equiv ((1+x)^p)^n imes(1+x)^{m}quad(mod;p) ]

    我们知道,在 ((1+x)^p) 的二项式展开式里,所有项前面都有一个 (C^{i}_{p}) ,那么除了 (1)(x^p) 两项,其他都可以被 (p) 整除。所以上式等同于:

    [(1+x^p)^n imes(1+x)^mequivsum^{n}_{i=1}C^{i}_{n}x^{ip};sum^{m}_{j=1}C^{j}_{m}x^jquad(mod;p) ]

    那么,在项 (x^{rp+s}) 项前的系数,用原二项式展开式推出来的式子表示应该是一样的:

    [C^{rp+s}_{np+m}equiv C^{r}_{n};C^{s}_{m}quad(mod;p) ]

    即为:

    [C^{n}_{m}equiv C^{lfloor frac{n}{p} floor}_{lfloor frac{m}{p} floor} imes C^{n\%p}_{m\%p}quad(mod;p) ]

    从另一个角度来看,其实是对 (n)(m) 进行 (p) 进制展开。

    MOD 及奇怪的小细节

    luogu P3807

    实际操作时 (n\%p) 项小于 (p) 可以直接算,但是 (lfloor frac{n}{p} floor) 项要继续递归求解。

    还有一个问题要注意,计算中, (n\%p) 是可能比 (m\%p) 大的,要怎么处理呢?

    从组合意义上,算 (0) 当然是没错,即为:一旦有 (n\%p)(m\%p) 大,就会是 (p) 的整倍数。

    结论很奇怪,我们还是来证明一下:

    [f 设 ;scr a=np+m,b=kp+squad f 其中;scr m>s,k>n\ f那么:; m C^{a}_{b}=frac{(kp+s)(kp+s-1)cdots(np+m+1)}{((k-n)p+(s-m))!} ]

    分子中有 (k-n)(i imes p) 项;当 (s<m) ,即 (s-m <0) 时,分母只有 (k-n-1)(i imes p) 项。

    可见最后有一个因子 (p) 被保留下来了。当然这时模 (p) 值为 (0)

    最后上代码:

    (frak code)

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    
    
    int t;
    int n,m,mod,ans;
    int fac[100005];
    
    int get_inv(int x,int p){
    	int ret=1;
    	int q=p-2;
    	while(q>0){
    		if(q&1) ret=(1ll*ret*x)%p;
    		x=(1ll*x*x)%p;
    		q>>=1;
    	}
    	return ret;	
    } 
    
    int c(int a,int b,int p){
    	if(b<a) return 0;	
    	return (1ll*(1ll*fac[b]*get_inv(fac[a],p))*get_inv(fac[b-a],p))%p;
    }
    
    int lucas(int a,int b,int p){
    	if(b==0) return 1;
    	return (1ll*lucas(a/p,b/p,p)*c(a%p,b%p,p)%p);
    }
    
    
    
    int main()
    {
    	fac[0]=1;
    	fac[1]=1;
    	scanf("%d",&t);
    	while(t--){
    		scanf("%d %d %d",&n,&m,&mod);
    		for(int i=2;i<=n+m;i++){
    			fac[i]=(1ll*fac[i-1]*i)%mod;
    		}
    		ans=lucas(n,n+m,mod);
    		printf("%d
    ",ans%mod);
    	}
    	return 0;
    }
    

    内容采用“知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议”进行许可。请您在转载时注明来源及链接


    (frak by;thorn\_)

  • 相关阅读:
    doker基础用法
    docker容器技术基础入门
    流模式
    装饰器模式
    适配器模式
    组合模式
    桥接模式
    观察者模式
    注册模式
    模板模式
  • 原文地址:https://www.cnblogs.com/thornblog/p/12235350.html
Copyright © 2011-2022 走看看