• 【模板】扩展卢卡斯(学习笔记)


    洛咕

    (C_n^m mod p),(1≤m≤n≤10^{18}),(2≤p≤1000000),不保证p是质数.

    分析:对于模数不是质数,但是可以分解成几个质数的乘积(唯一分解定理)的情况,与古代猪文这道题有点类似.古代猪文中我们直接求出了原式分别模分解出的质数之后的值,然后把这些值用中国剩余定理(CRT)合并.本题也可以采取类似方法,假设(p=p_1^{c_1}p_2^{c_2}...p_k^{c_k})于是题目转化为了求(frac{n!}{m!(n-m)!} mod p_i^{c_i}),但值得注意的是(p_i^{c_i})是质数的幂,而非质数.

    现在我们来考虑如何求(frac{n!}{m!(n-m)!} mod p_i^{c_i}).因为模数不是质数,所以不能直接用逆元求,考虑是否能够把所有与(p_i^{c_i})不互质的数单独拎出来,对于(n!),提出一个数,总共有(frac{n}{p_i}),而剩下的能做贡献的只有([frac{n}{p_i}]!)(向下取整),对此进行递归求解即可.

    接下来考虑如何处理不是(p_i)的倍数的数,直接算肯定会超时,考虑它具有循环节的这一特点,每一个循环节的余数都相同,故只需处理一个循环节,其他循环节直接调用结果即可,可能最后还会有一小部分不构成完整循环节,这一部分可以直接暴算.

    #include<bits/stdc++.h>
    #define LL long long
    #define rg register
    using namespace std;
    inline LL read(){
        LL s=0,w=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
        return s*w;
    }
    inline LL ksm(LL a,LL b,LL c){
        rg LL cnt=1;
        while(b){
    		if(b&1)cnt=(1ll*cnt*a)%c;
    		a=(1ll*a*a)%c;
    		b>>=1;
        }
        return cnt;
    }
    inline void exgcd(LL a,LL b,LL &x,LL &y){
        if(!b){x=1;y=0;return;}
        exgcd(b,a%b,y,x);y-=(a/b)*x;
    }
    inline LL inv(LL a,LL b){//扩欧求逆元
        rg LL x,y;exgcd(a,b,x,y);
        return (x%b+b)%b;
    }
    inline LL CRT(LL x,LL p,LL mod){return inv(p/mod,mod)*(p/mod)*x;}
    //中国剩余定理合并
    inline LL fac(LL x,LL a,LL b){//处理阶乘的模
        if(x==0)return 1;rg LL ans=1;
        for(rg LL i=1;i<=b;i++)
        	if(i%a)ans*=i,ans%=b;
        ans=ksm(ans,x/b,b);
        for(rg LL i=1;i<=x%b;i++)
        	if(i%a)ans*=i,ans%=b;
        return (1ll*ans*fac(x/a,a,b))%b;
    }
    inline LL C(LL n,LL m,LL a,LL b){
    //求组合数模b,b即为pi^ci,a即为pi
        if(n<m)return 0;
        rg LL N=fac(n,a,b),M=fac(m,a,b),NM=fac(n-m,a,b),sum=0;
    //分别求n!,m!和(n-m)! mod b的值
        for(rg LL i=n;i;i/=a)sum+=i/a;
        for(rg LL i=m;i;i/=a)sum-=i/a;
        for(rg LL i=n-m;i;i/=a)sum-=i/a;
        return 1ll*(N*ksm(a,sum,b))%b*(inv(M,b)*inv(NM,b))%b;
    }
    inline void exlucas(LL n,LL m,LL p){
        rg LL M=p,ans=0;
        for(LL i=2;i*i<=p;i++){//对p质因数分解
    		LL tot=1;//tot即为pi^ci
    		while(M%i==0)tot*=i,M/=i;
    		ans+=CRT(C(n,m,i,tot),p,tot),ans%=p;
    //求出每个部分的值然后合并
        }
        if(M>1)ans+=CRT(C(n,m,M,M),p,M),ans%=p;
    //可能最后还剩下了一个质数未分解出来
        printf("%lld
    ",ans);
    }
    int main(){
        rg LL n=read(),m=read(),p=read();
        exlucas(n,m,p);
        return 0;
    }
    
    

    练习题---[国家集训队]礼物

  • 相关阅读:
    [VBA]根据身份证号码计算年龄的Excel函数
    [VBA]发布一个计算桩号之差的Excel自定义函数(VBA)
    [VBA]用一个简单例子说明如何在Excel中自定义函数
    元素定位工具:Chrome浏览器ChroPath插件
    68 个 Python 内置函数
    Python错误重试
    jenkins 中展示漂亮的 HTML 测试报告
    python解决接口数据使用了RSA加密和签名
    Python装饰器用法
    Pycharm中文版教程
  • 原文地址:https://www.cnblogs.com/PPXppx/p/10828291.html
Copyright © 2020-2023  润新知