zoukankan      html  css  js  c++  java
  • 扩展卢卡斯定理(Exlucas)

    题目链接

    戳我

    前置知识

    1. 中国剩余定理(crt)或扩展中国剩余定理(excrt)
    2. 乘法逆元
    3. 组合数的基本运用
    4. 扩展欧几里得(exgcd)

    说实话Lucas真的和这个没有什么太大的关系,但是Lucas还是要学学的:戳我

    正文

    题目是要求:

    [c_n^m mod p ]

    如果这个p是质数的话那太简单了,直接Lucas就好了,但问题是现在p不一定是一个质数。

    我们令 (P=prod {p_i}^{c_i})

    我们如果知道每个(c_n^m mod p_i^{c_i})的值的话就可以根据中国剩余定理求出答案
    那我们怎么求出这个值呢?
    我们可以将(c_n^m)写成(frac{n!}{m!(n-m)!})

    现在我们可以处理阶乘的模。那么如何处理阶乘的模呢?

    举个经典例子:
    (p=3,n=19,c=2)
    我们可以吧式子写成这样:

    [(19*18*17*16*15*14*13*12*11*10*9*8*7*6*5*4*3*2*1) ]

    [=(19*17*16*14*13*11*10*8*7*5*4*2*1)*3^6*6! ]

    我们可以将他分为几个部分

    [19*(17*16*14*13*11*10)*(8*7*5*4*2*1)*3^6*6! ]

    我们会发现对于每一个整的部分如((8*7*5*4*2*1))的模数都是一样的,于是这一块我们可以运用快速幂,而剩余的(19)我们可以进行暴力。对于(6!)我们可以继续递归求解,那么怎么分组呢
    我们可以把每一段的范围定为(p^c)。差不多就这样吧。

    code

    #include<bits/stdc++.h>
    #define rg register
    #define int long long
    #define file(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout);
    using namespace std;
    int read(){
        int x=0,f=1;
        char c=getchar();
        while(c<'0'||c>'9') f=(c=='-')?-1:1,c=getchar();
        while(c>='0'&&c<='9') x=x*10+c-48,c=getchar();
        return f*x;
    }
    inline void exgcd(int a,int b ,int &x,int &y){
        if(!b){x=1,y=0;return;}
        exgcd(b,a%b,x,y);
        int t=x;
        x=y,y=t-(a/b)*y;
    }
    inline int inv(int a,int b){
        int x,y;
        return exgcd(a,b,x,y),(x%b+b)%b;
    }
    inline int ksm(int a,int b,int p){
        int ans=1;
        while(b){
            if(b&1)
                ans=a*ans%p;
            a=a*a%p;
            b>>=1;
        }
        return ans%p;
    }
    inline int crt(int x,int p,int mod){
        return inv(p/mod,mod)*(p/mod)*x;
    }
    inline int fac(int x,int a,int b){
        if(!x)
            return 1;
        int ans=1;
        for(int i=1;i<=b;i++)
        if(i%a)
                ans*=i,ans%=b;
        ans=ksm(ans,x/b,b);
        for(int i=1;i<=x%b;i++)
            if(i%a)
                ans*=i,ans%=b;
        return ans*fac(x/a,a,b)%b;
    }
    inline int C(int n,int m,int a,int b){
        int N=fac(n,a,b),M=fac(m,a,b),Z=fac(n-m,a,b),sum=0;
        for(int i=n;i;i=i/a)
            sum+=i/a;
        for(int i=m;i;i=i/a)
            sum-=i/a;
        for(int i=n-m;i;i=i/a)
            sum-=i/a;
        return N*ksm(a,sum,b)%b*inv(M,b)%b*inv(Z,b)%b;
    }
    inline void exlucas(int n,int m,int p){
        int t=p,ans=0;
        for(int i=2;i*i<=p;i++){
            int k=1;
            while(t%i==0)
                k*=i,t/=i;
            ans+=crt(C(n,m,i,k),p,k),ans%=p;
        }
        if(t>1)
            ans+=crt(C(n,m,t,t),p,t),ans%=p;
        printf("%d",ans%p);
    }
    main(){
        int n=read(),m=read(),p=read();
        exlucas(n,m,p);
        return 0;
    }
    
  • 相关阅读:
    冒泡排序
    三种for循环遍历
    打印一年中的月历
    基于主主复制的mysql双机热备+keepalived实现高可用性
    docker实现apache+php容器和mysql容器独立运行
    XML和JSON
    PHP表单
    【翻译-Docker】Post-installation steps for Linux
    【翻译】docker install
    小计划
  • 原文地址:https://www.cnblogs.com/hbxblog/p/10211177.html
Copyright © 2011-2022 走看看