zoukankan      html  css  js  c++  java
  • 浅谈Lucas定理(卢卡斯定理)

    Lucas定理(卢卡斯定理)

    ( ext{Lucas})定理是用于求 (C^m_n\% p) 的一种算法。

    定理

    (p)为素数时,有(C_{n}^{m} equiv C_{n\%p}^{m\%p} imes C_{n/p}^{m/p}( ext{mod} p))

    证明

    (n = s imes p + q,m = t imes p + r),有

    [C_n^m=C_{a imes p+q}^{t imes p+r} ]

    现在我们就要证明(C_{s imes p+q}^{t imes p+r}equiv C_s^t imes C_q^r( ext{mod} p))

    要证明这个定理,首先我们要知道费马小定理:当(p)为素数时,(x^pequiv x ( ext{mod} p))

    由此我们可以知道((1+x)^pequiv 1 + x( ext{mod} p)),并且(x^p+1equiv x+1( ext{mod} p)),所以((x+1)^pequiv x^p+1)

    由上面这个性质,得((x+1)^nequiv(x+1)^{s imes p+q}equiv((x+1)^p)^s imes (x+1)^qequiv (x^p+1)^s imes (x+1)^q)

    用二项式定理展开(equiv sumlimits_{i=0}^sC_s^i x ^{i imes p} imessumlimits_{j=0}^qC_q^jx^j)

    由上面这些可得((x+1)^pequivsumlimits_{i=0}^sC_s^i x ^{i imes p} imessumlimits_{j=0}^qC_q^jx^j)

    考虑把两边的多项式展开一下,那么两边肯定都有(x^m)(x^{t imes p+r})这一项(这是最上面的假设)

    左边的(x^m)的系数,根据上面的性质推出来,应该是(C^m_n)

    然后右边当(i=t,j=r)的时候才会有这一项,所以这一项的系数就是(C^t_s∗C^r_q)

    然后又因为(s=n/p,t=n\%p,q=m/p,r=m\%p)

    所以(C_n^mequiv C^{m/p}_{n/p} imes C^{m\%p}_{n\%p}( ext{mod} p))

    代码

    inline LL lucas(LL n, LL m, LL p) {
        if(!m) return 1;
        return C(n % p, m % p, p) * lucas(n / p, m / p, p) % p;
    }
    

    例题

    此代码为洛谷P3807的代码,题目条件稍有不同,请注意

    /*
    Author:loceaner
    */
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define int long long
    using namespace std;
    
    const int A = 2e5 + 11;
    const int B = 1e6 + 11;
    const int inf = 0x3f3f3f3f;
    
    inline int read() {
    	char c = getchar(); int x = 0, f = 1;
    	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
    	for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    	return x * f;
    }
    
    int T, n, m, p, fac[A];
    
    inline int power(int a, int b) {
    	int res = 1;
    	while (b) {
    		if (b & 1) res = res * a % p;
    		a = a * a % p, b >>= 1;
    	}
    	return res;
    }
    
    inline int C(int n, int m) {
    	if (m > n) return 0;
    	return fac[n] * power(fac[m], p - 2) % p *  power(fac[n - m], p - 2) % p;
    }
    
    inline int Lucas(int n, int m) {
    	if (!m) return 1;
    	return C(n % p, m % p) * Lucas(n / p, m / p) % p; 
    }
    
    inline void init() {
    	int maxn = p;
    	fac[0] = 1;
    	for (int i = 1; i <= maxn; i++) fac[i] = fac[i - 1] * i % p;
    }
    
    signed main() {
    	T = read();
    	while (T--) {
    		n = read(), m = read(), p = read();
    		n += m;
    		init();
    		cout << (Lucas(n, m) + p) % p << '
    ';
    	}
    	return 0;
    }
    

    证明参考自bztMinamoto的博客

  • 相关阅读:
    java中传值与传引用
    microsofr visual studio编写c语言
    openfile学习笔记
    在 Windows 和 Linux(Gnome) 环境下 从命令界面打开网页的方式
    使用vsphere client 克隆虚拟机
    route命令
    linux rpm问题:怎样查看rpm安装包的安装路径
    【leetcode】415. 字符串相加
    【leetcode】面试题 17.01. 不用加号的加法
    【leetcode】989. 数组形式的整数加法
  • 原文地址:https://www.cnblogs.com/loceaner/p/12746161.html
Copyright © 2011-2022 走看看