精华内容
下载资源
问答
  • 组合数的性质: C(n,m)=C(n,n-m); C(n,m)=n!/(m!(n-m)!); 组合数的递推公式: C(n,m)= C(n-1,m-1)+C(n-1,m); 组合数一般数值较大,题目会要求取模;而求组合数的过程中一般会用到除法,所以会涉及除法取模的...

     

    组合数的性质:

    C(n,m)=C(n,n-m);

    C(n,m)=n!/(m!(n-m)!);

    组合数性质

    组合数的递推公式:


    C(n,m)=  C(n-1,m-1)+C(n-1,m);

     

    组合数一般数值较大,题目会要求取模;而求组合数的过程中一般会用到除法,所以会涉及除法取模的知识;

    在除法取模的过程中,一般会求一个乘法逆元;

    乘法逆元的定义:满足a*k≡1 (mod p)的k值就是a关于p的乘法逆元;

    求乘法逆元的方法:

    (b/a)modp;(a|b)p为质数;

    1.欧拉定理或者费马小定理:

      费马小定理是欧拉定理的特殊情况;

      费马小定理的定义及证明:链接

        由得b/a=(b/a)*(ap-1modp)=b/a*ap-1modp=b*ap-2modp;

        除法就被消去了;

        而这样做还有一个问题就是p-2一般很大,(因为p一般都取1e9+7,NND,我记得有次BC的题是1e8+7直接把我坑惨了);这时就用快速幂求啦;

        附上快速幂的模板:

        

    ll fsat_pow(ll a,ll b)
    {
        ll s=1,base=a;
        while(b)
        {
            if(b&1)
            {
                s*=base;
                s%=mod;
            }
            base*=base;
            base%=mod;
            b=(b>>1);
        }
        return s;
    }

     

     

    2.扩展欧几里得算法:

     

     

    当n,m都很大不能一个一个数相乘得到时,这时就需要Lucas定理了;(有心情有时间再来写)

     

    转载于:https://www.cnblogs.com/zhangchengc919/p/5404459.html

    展开全文
  • Lucas定理是用来求 c(n,m) mod p,p为素数的值。 对于C(n, m) mod p。...应用:大组合数求模 表达式C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)%p 因为p为素数,所以这类题都可以用费马小定理计算逆元。当然...

    Lucas定理是用来求 c(n,m) mod p,p为素数的值。
    对于C(n, m) mod p。这里的n,m,p(p为素数)都很大的情况。就不能再用C(n, m) = C(n - 1,m) + C(n - 1, m - 1)的公式递推了。
    应用:大组合数求模
    表达式C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)%p

    因为p为素数,所以这类题都可以用费马小定理计算逆元。当然也可以用扩展欧几里得。
    常见题型:
    一 . n ,m较大,mod 为素数且较小
    51nod1120卡特兰数+卢斯卡定理
    因为mod较小,可以预处理出小于mod的阶乘。
    Code:

    #include <bits/stdc++.h>
    #define LL long long
    using namespace std;
    const LL MOD = 10007;
    const int AX = 1e6+66;
    LL f[AX];
    void init( LL p ) {
        f[0] = 1LL;
        for( LL i = 1 ; i <= p ; i++ ) f[i] = ( f[i-1] * i ) % p;
    }
    LL quick( LL a , LL b , LL p ){
        LL ans = 1LL;
        while( b ){
            if( b & 1 ) ans = ( ans * a ) % p;
            a = ( a * a ) % p;
            b >>= 1 ;
        }
        return ans ;
    } 
    LL C( LL n , LL m , LL p ){
        if( m < 0 || n < m ) return 0 ;
        return f[n] * quick( f[m] , p - 2 , p ) % p * quick( f[n-m] , p - 2 , p ) % p ;
    }
    LL Lucas( LL n , LL m , LL p ){
        return m ? Lucas( n / p , m / p , p ) * C( n % p , m % p , p ) % p : 1 ;
    }
    int main(){
       LL n;
       init(MOD);
       scanf("%lld",&n);
       n--;
       LL ans = Lucas(2*n,n,MOD)*quick(n+1,MOD-2,MOD)%MOD;
       printf("%lld\n",2*ans%MOD);
       return 0;
    }
    

    二 . n , m ,mod 较大,mod为素数
    例题:mod<=1e9,n<=1e9, m<=1e4
    Code:

    #include <iostream>
    #include <cstdio>
    #define LL long long
    using namespace std;
    
    LL quick( LL a , LL b , LL p ){
        LL ans = 1LL;
        while( b ){
            if( b & 1 ) ans = ( ans * a ) % p ;
            a = ( a * a ) % p ;
            b >>= 1 ;
        }
        return ans ;
    }
    
    LL fac( LL x , LL p ){
        LL ans = 1;
        for( int i = 2 ; i <= x ; i ++ ) ans = ( ans * i ) % p;
        return ans;
    }
    
    LL C( LL n , LL m , LL p ){
        if( m < 0 || n < m ) return 0;
        if( m == 0 ) return 1 ;
        m = min( m , n - m );
        LL a = 1LL , b = 1LL;
        for( LL i = 1 ; i <= m ; i++ ){
            a =   a * (n - i + 1) % p ;
            b = ( b * i ) % p;
        }
        return a * quick( b , p - 2 , p ) % p ; 
    }
    
    
    LL Lucas( LL n , LL m , LL p ){
        return m ? C( n % p , m % p , p ) * Lucas( n / p , m / p , p ) % p : 1 ;
    }
    
    int main(){
        int T;
        LL n , m , p;
        scanf("%d",&T);
        while( T-- ){
            scanf("%lld%lld%lld",&n,&m,&p);
            LL res = Lucas( n , m , p );
            printf("%lld\n",res);
        }
        return 0 ; 
    }

    三 . n , m 很大且mod为几个不同素数的乘积
    这里写图片描述
    例题
    需要注意的是因为数非常大,所以如果使用费马小定理计算逆元,快速幂时需要使用快速乘法防止中间爆LL
    Code:

    #include <bits/stdc++.h>
    using namespace std;
    const int INF = 0x3f3f3f3f;
    const int MOD =  1e9+7;
    const int MAXN = 2e5+10;
    #define LL long long
    LL mul( LL a , LL b , LL p ){
        LL ans = 0LL;
        while( b ){
            if( b & 1 ) ans = ( ans + a ) % p ;
            a = ( a + a ) % p ;
            b >>= 1 ;
        }
        return ans ;
    }
    
    LL quick( LL a , LL b , LL p ){
        LL ans = 1LL;
        while( b ){
            if( b & 1 ) ans = mul( ans , a , p ) % p;
            a = mul( a , a , p ) % p;
            b >>= 1 ;
        }
        return ans ;
    } 
    
    
    LL fac( LL x , LL p ){
        LL ans = 1;
        for( LL i = 2 ; i <= x ; i ++ ) ans = ( ans * i ) % p;
        return ans;
    }
    
    LL C( LL n , LL m , LL p ){
        if( m < 0 || n < m ) return 0 ;
        return fac( n , p ) * quick( fac( m , p ) , p - 2 , p ) % p * quick( fac( n - m , p ) , p - 2 , p ) % p ;
    }
    
    LL CRT( LL *a , LL *m , int n ){
        LL M = 1LL , res = 0LL;
        for( int i = 0 ; i < n ; i++ ) M *= m[i];
        for( int i = 0 ; i < n ; i++ ){
            LL w = M / m[i];
            res = ( res + mul( w * quick( w , m[i] - 2 , m[i] ) , a[i] , M ) ) % M;
        }
        return ( res + M ) % M;
    }
    
    LL Lucas( LL n , LL m , LL p ){
        return m ? Lucas( n / p , m / p , p ) * C( n % p , m % p , p ) % p : 1 ;
    }
    int main(){
        LL n,m;
        LL p[15];
        LL a[15];
        int T,k;
        scanf("%d",&T);
        while(T--){
            scanf("%lld%lld%d",&n,&m,&k);
            for( int i = 0 ; i < k ; i++ ){
                scanf("%lld",&p[i]);
            }
            for( int i = 0 ; i < k ; i++ ){
                a[i] = Lucas( n , m , p[i] );
            }
            printf("%lld\n",CRT(a,p,k));
        }
        return 0;
    }

    下面贴一个扩展欧几里得求逆元版本的:

    #include <bits/stdc++.h>
    using namespace std;
    const int INF = 0x3f3f3f3f;
    const int MOD =  1e9+7;
    const int MAXN = 2e5+10;
    #define LL long long
    LL qmul(LL a,LL p,LL m){
        LL tmp = 0;
        while(p){
            if(1&p) tmp = (tmp+a)%m;
            a = (a+a)%m;
            p>>=1;
        }
        return tmp;
    }
    void exgcd(LL a,LL b,LL &x,LL &y,LL &d){
        if(!b) d=a,x = 1,y=0;
        else exgcd(b,a%b,y,x,d),y-=(a/b)*x;
    }
    LL inv(LL a,LL p){
        LL x,y,d;
        exgcd(a,p,x,y,d);
        return d==1?(x+p)%p:-1;
    }
    LL fat(LL x,LL p){
        LL ans = 1;
        for( int i = 2 ; i <= x ; i ++ ) ans = ( ans * i ) % p;
        return ans;
    }
    LL c(LL n,LL m,LL p){
        if (m < 0 || m > n) return 0; 
        return fat(n,p)*inv(fat(m,p),p)%p*inv(fat(n-m,p),p)%p;
    }
    LL crt(LL *a,LL *m,int n){
        LL M = 1,res = 0;
        for( int i = 0 ; i < n ; i++ ) M*=m[i];
        for( int i = 0 ; i < n ; i++ ){
            LL w = M/m[i];
            res = (res+qmul(w*inv(w,m[i]),a[i],M))%M;
        }
        return (res+M)%M;
    }
    LL Lucas(LL n,LL m,LL p){
        return m?Lucas(n/p,m/p,p)*c(n%p,m%p,p)%p:1;
    }
    int main(){
        LL n,m;
        LL p[15];
        LL a[15];
        int T,k;
        scanf("%d",&T);
        while(T--){
            scanf("%lld%lld%d",&n,&m,&k);
            for( int i = 0 ; i < k ; i++ ){
                scanf("%lld",&p[i]);
            }
            for( int i = 0 ; i < k ; i++ ){
                a[i] = Lucas( n , m , p[i] );
            }
            printf("%lld\n",crt(a,p,k));
        }
        return 0;
    }

    4.n , m 很大且mod不一定为素数
    解决方法就是将mod分解成 (p1^c1)(p2^c2) ***(pk^ck) (k 个素数 )
    求 C(n,m)% pi^ci .
    下面是求 C(n,m)% pi^ci的方法
    这里写图片描述
    待填坑。遇到题再补充。

    展开全文
  • 组合数取模在ACM竞赛中是一...组合数取模就是求的值,当然根据,和的取值范围不同,采取的方法也是不一样。 接下来我们来看一些常见的取值情况: (1)和 对于这种情况,我们可以直接n2的求可以用组和数的

       组合数取模在ACM竞赛中是一个很重要也很常见的问题,之前碰到的很多时候都因为数据太大而束手无策,今天就来详细总结一下组合数相关的一些问题。


    组合数取模就是求的值,当然根据的取值范围不同,采取的方法也是不一样。


    接下来我们来看一些常见的取值情况:


    (1)


    对于这种情况,我们可以直接n2的求可以用组和数的递推公式c(n,m) =c(n - 1,m) + c(n – 1, m – 1)来计算,要么就用


    组合数公式加逆元计算。


    (2),并且是素数

       

        这个问题有个叫做Lucas的定理,定理描述是,如果




        那么得到


        


        这样分别计算就行啦(其实ni,mi相当于p进制下第i位的值)


    (3),并且可能为合数


    因为是合数,所以不能直接求逆元,那么可以先采取暴力分解,得到n!中素因子的指数,然后快速幂就行啦


    #include <iostream>  
    #include <string.h>  
    #include <stdio.h>  
      
    using namespace std;  
    typedef long long LL;  
    const int N = 200005;  
      
    bool prime[N];  
    int p[N];  
    int cnt;  
      
    void isprime(){  
        cnt = 0;  
        memset(prime,true,sizeof(prime));  
        for(int i=2; i<N; i++){  
            if(prime[i]){  
                p[cnt++] = i;  
                for(int j=i+i; j<N; j+=i)  
                    prime[j] = false;  
            }  
        }  
    }  
      
    LL quick_mod(LL a,LL b,LL m){  
        LL ans = 1;  
        a %= m;  
        while(b){  
            if(b & 1) 
                ans = ans * a % m;
            b >>= 1;  
            a = a * a % m;  
        }  
        return ans;  
    }  
      
    LL Work(LL n,LL p){  
        LL ans = 0;  
        while(n){  
            ans += n / p;  
            n /= p;  
        }  
        return ans;  
    }  
      
    LL Solve(LL n,LL m,LL P){  
        LL ans = 1;  
        for(int i=0; i<cnt && p[i]<=n; i++){  
            LL x = Work(n, p[i]);  
            LL y = Work(n - m, p[i]);  
            LL z = Work(m, p[i]);  
            x -= (y + z);  
            ans *= quick_mod(p[i],x,P);  
            ans %= P;  
        }  
        return ans;  
    }  
      
    int main(){  
        int T;  
        isprime();  
        cin>>T;  
        while(T--){  
            LL n,m,P;  
            cin>>n>>m>>P;  
            n += m - 2;  
            m--;  
            cout<<Solve(n,m,P)<<endl;  
        }  
        return 0;  
    } 


    展开全文
  • 组合数取模

    2017-05-08 10:41:20
    组合数取模在ACM竞赛中是一个很重要的问题,很多选手因为数据太大而束手无策,今天就来详细讲解它。... 这个问题比较简单,组合数的计算可以靠杨辉三角,那么由于和的范围小,直接两层循环即可。   (2)

    组合数取模在ACM竞赛中是一个很重要的问题,很多选手因为数据太大而束手无策,今天就来详细讲解它。

     

    组合数取模就是求的值,当然根据的取值范围不同,采取的方法也不一样。

     

    接下来,我们来学习一些常见的取值情况

     

    (1)

     

         这个问题比较简单,组合数的计算可以靠杨辉三角,那么由于的范围小,直接两层循环即可。

     

    (2),并且是素数

     

         这个问题有个叫做Lucas的定理,定理描述是,如果

     

         

     

         那么得到

     

         

       

         这样然后分别求,采用逆元计算即可。

     

     

    题目:http://acm.fzu.edu.cn/problem.php?pid=2020

     

    题意:,其中,并且是素数。

     

    代码:

    #include <iostream>  
    #include <string.h>  
    #include <stdio.h>  
      
    using namespace std;  
    typedef long long LL;  
      
    LL n,m,p;  
      
    LL quick_mod(LL a, LL b)  
    {  
        LL ans = 1;  
        a %= p;  
        while(b)  
        {  
            if(b & 1)  
            {  
                ans = ans * a % p;  
                b--;  
            }  
            b >>= 1;  
            a = a * a % p;  
        }  
        return ans;  
    }  
      
    LL C(LL n, LL m)  
    {  
        if(m > n) return 0;  
        LL ans = 1;  
        for(int i=1; i<=m; i++)  
        {  
            LL a = (n + i - m) % p;  
            LL b = i % p;  
            ans = ans * (a * quick_mod(b, p-2) % p) % p;  
        }  
        return ans;  
    }  
      
    LL Lucas(LL n, LL m)  
    {  
        if(m == 0) return 1;  
        return C(n % p, m % p) * Lucas(n / p, m / p) % p;  
    }  
      
    int main()  
    {  
        int T;  
        scanf("%d", &T);  
        while(T--)  
        {  
            scanf("%I64d%I64d%I64d", &n, &m, &p);  
            printf("%I64d\n", Lucas(n,m));  
        }  
        return 0;  
    }  

    由于上题的比较大,所以组合数只能一个一个计算,如果的范围小点,那么就可以进行阶乘预处理计算了。

     

    (3),并且可能为合数

     

        这样的话先采取暴力分解,然后快速幂即可。

     

    题目:http://acm.nefu.edu.cn/JudgeOnline/problemshow.php?problem_id=628

     

    代码:

    #include <iostream>  
    #include <string.h>  
    #include <stdio.h>  
      
    using namespace std;  
    typedef long long LL;  
      
    LL n,m,p;  
      
    LL quick_mod(LL a, LL b)  
    {  
        LL ans = 1;  
        a %= p;  
        while(b)  
        {  
            if(b & 1)  
            {  
                ans = ans * a % p;  
                b--;  
            }  
            b >>= 1;  
            a = a * a % p;  
        }  
        return ans;  
    }  
      
    LL C(LL n, LL m)  
    {  
        if(m > n) return 0;  
        LL ans = 1;  
        for(int i=1; i<=m; i++)  
        {  
            LL a = (n + i - m) % p;  
            LL b = i % p;  
            ans = ans * (a * quick_mod(b, p-2) % p) % p;  
        }  
        return ans;  
    }  
      
    LL Lucas(LL n, LL m)  
    {  
        if(m == 0) return 1;  
        return C(n % p, m % p) * Lucas(n / p, m / p) % p;  
    }  
      
    int main()  
    {  
        int T;  
        scanf("%d", &T);  
        while(T--)  
        {  
            scanf("%I64d%I64d%I64d", &n, &m, &p);  
            printf("%I64d\n", Lucas(n,m));  
        }  
        return 0;  
    }  

    由于上题的比较大,所以组合数只能一个一个计算,如果的范围小点,那么就可以进行阶乘预处理计算了。

     

    (3),并且可能为合数

     

        这样的话先采取暴力分解,然后快速幂即可。

     

    题目:http://acm.nefu.edu.cn/JudgeOnline/problemshow.php?problem_id=628

     

    代码:

    #include <iostream>  
    #include <string.h>  
    #include <stdio.h>  
      
    using namespace std;  
    typedef long long LL;  
    const int N = 200005;  
      
    bool prime[N];  
    int p[N];  
    int cnt;  
      
    void isprime()  
    {  
        cnt = 0;  
        memset(prime,true,sizeof(prime));  
        for(int i=2; i<N; i++)  
        {  
            if(prime[i])  
            {  
                p[cnt++] = i;  
                for(int j=i+i; j<N; j+=i)  
                    prime[j] = false;  
            }  
        }  
    }  
      
    LL quick_mod(LL a,LL b,LL m)  
    {  
        LL ans = 1;  
        a %= m;  
        while(b)  
        {  
            if(b & 1)  
            {  
                ans = ans * a % m;  
                b--;  
            }  
            b >>= 1;  
            a = a * a % m;  
        }  
        return ans;  
    }  
      
    LL Work(LL n,LL p)  
    {  
        LL ans = 0;  
        while(n)  
        {  
            ans += n / p;  
            n /= p;  
        }  
        return ans;  
    }  
      
    LL Solve(LL n,LL m,LL P)  
    {  
        LL ans = 1;  
        for(int i=0; i<cnt && p[i]<=n; i++)  
        {  
            LL x = Work(n, p[i]);  
            LL y = Work(n - m, p[i]);  
            LL z = Work(m, p[i]);  
            x -= (y + z);  
            ans *= quick_mod(p[i],x,P);  
            ans %= P;  
        }  
        return ans;  
    }  
      
    int main()  
    {  
        int T;  
        isprime();  
        cin>>T;  
        while(T--)  
        {  
            LL n,m,P;  
            cin>>n>>m>>P;  
            n += m - 2;  
            m--;  
            cout<<Solve(n,m,P)<<endl;  
        }  
        return 0;  
    }  

    接下来看一些关于组合数取模的典型题目。

     

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=3944

     

    分析:组合数取模的典型题目,用Lucas定理,注意要阶乘预处理,否则会TLE的。

     

     

    题目:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4536

     

    题意:给一个集合,一共个元素,从中选取个元素,选出的元素中没有相邻的元素的选法一共有多少种?

     

    分析:典型的隔板法,最终答案就是。然后用Lucas定理处理即可。

     

     

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=4373

     

    题意:for循环嵌套,有两种形式,第一类从1开始到,第二类从上一层循环当前数开始到,第一层一定

         是第一种类型,求总的循环的次数对364875103取余的结果。

     

    分析:首先可以看出,每一个第一类循环都是一个新的开始,与前面的状态无关,所以可以把个嵌套分为几个不

         同的部分,每一个部分由第一类循环开始,最终结果相乘即可。剩下的就是第二类循环的问题,假设一个

         层循环,最大到,分析一下得到如下结果

        

         (1)只有一层,则循环次数为

     

         (2)只有前两层,则循环次数为

     

             

     

         (3)只有前三层,则循环次数为

     

             

     

          由此得到结论:第的循环次数为



    代码:

    #include <iostream>  
    #include <string.h>  
    #include <stdio.h>  
      
    using namespace std;  
    typedef long long LL;  
      
    const int N = 25;  
    const int MOD1 = 97;  
    const int MOD2 = 3761599;  
    const int MOD = MOD1 * MOD2;  
      
    int m,n,k;  
    int a[N];  
    LL fac1[MOD1+10];  
    LL fac2[MOD2+10];  
    LL inv1,inv2;  
      
    LL quick_mod(LL a,LL b,LL m)  
    {  
        LL ans = 1;  
        a %= m;  
        while(b)  
        {  
            if(b & 1)  
            {  
                ans = ans * a % m;  
                b--;  
            }  
            b >>= 1;  
            a = a * a % m;  
        }  
        return ans;  
    }  
      
    LL C(LL n,LL m,LL p,LL fac[])  
    {  
        if(n < m) return 0;  
        return fac[n] * quick_mod(fac[m] * fac[n-m], p - 2, p) % p;  
    }  
      
    LL Lucas(LL n,LL m,LL p,LL fac[])  
    {  
        if(m == 0) return 1;  
        return C(n % p, m % p, p, fac) * Lucas(n / p, m / p, p, fac);  
    }  
      
    void Init()  
    {  
        fac1[0] = fac2[0] = 1;  
        for(int i=1; i<MOD1; i++)  
            fac1[i] = (fac1[i-1] * i) % MOD1;  
        for(int i=1; i<MOD2; i++)  
            fac2[i] = (fac2[i-1] * i) % MOD2;  
        inv1 = MOD2 * quick_mod(MOD2, MOD1-2, MOD1);  
        inv2 = MOD1 * quick_mod(MOD1, MOD2-2, MOD2);  
    }  
      
    int main()  
    {  
        Init();  
        int T, tt = 1;  
        scanf("%d",&T);  
        while(T--)  
        {  
            scanf("%d%d%d",&n,&m,&k);  
            for(int i=0; i<k; i++)  
                scanf("%d",&a[i]);  
            a[k] = m;  
            LL ans = 1;  
            for(int i=0; i<k; i++)  
            {  
                LL m1 = Lucas(a[i+1] - a[i] + n - 1, a[i+1] - a[i], MOD1, fac1);  
                LL m2 = Lucas(a[i+1] - a[i] + n - 1, a[i+1] - a[i], MOD2, fac2);  
                LL mm = (m1 * inv1 + m2 * inv2) % MOD;  
                ans = ans * mm % MOD;  
            }  
            printf("Case #%d: ",tt++);  
            cout<<ans<<endl;  
        }  
        return 0;  
    }  


    题目:http://acm.hdu.edu.cn/showproblem.php?pid=4349

     

    题意:中有多少个奇数,其中

     

    分析:其实组合数判断奇偶性有一个优美的结论

              

                如果,那么为奇数,否则为偶数

     

                当然本题要判断的组合数很多,所以不能用上述结论,只能另辟蹊径。由于是判断奇偶性,那么就是判断

         是否为1,利用Lucas定理,先把化为二进制,这样它们都是01序列了。我们又知道

         。这样中为0的地方对应的中的位置只有一种可能,那就是0

     

          这样我们可以不用管中为0的地方,只考虑中为1的位置,可以看出,中为1的位置对应的中为0

          或1,其结果都是1,这样答案就是:1<<(二进制表示中1的个数)

     

    代码:


    #include <iostream>  
    #include <string.h>  
    #include <stdio.h>  
      
    using namespace std;  
      
    int main()  
    {  
        int n;  
        while(scanf("%d",&n)!=EOF)  
        {  
            int cnt = 0;  
            while (n)  
            {  
                if (n & 1) cnt++;  
                n >>= 1;  
            }  
            printf("%d\n",1<<cnt);  
        }  
        return 0;  
    }  

    题目:http://61.187.179.132/JudgeOnline/problem.php?id=1951

     

    题意:给定两个正整数,其中,求下面表达式的值

     

         

     

    分析:由于999911659是素数,用费马小定理降幂得到

     

         

     

         现在关键是求

        

         

     

         那么我们枚举分别计算,但是模的是合数,所以对999911658进行分解得到

     

         ,那么分别求,即

     

          

     

         然后进一步得到同余方程组为

     

           

     

         再通过中国剩余定理(CRT)可以求得最终答案

     

    代码:


    #include <iostream>  
    #include <string.h>  
    #include <stdio.h>  
      
    using namespace std;  
    typedef long long LL;  
      
    const int P = 999911659;  
      
    LL a[5] = {0, 0, 0, 0};  
    LL m[5] = {2, 3, 4679, 35617};  
    LL fac[5][36010];  
    LL N, G;  
      
    void Init()  
    {  
        for(int i=0; i<4; i++)  
        {  
            fac[i][0] = 1;  
            for(int j=1; j<36010; j++)  
                fac[i][j] = fac[i][j-1] * j % m[i];  
        }  
    }  
      
    LL quick_mod(LL a,LL b,LL m)  
    {  
        LL ans = 1;  
        a %= m;  
        while(b)  
        {  
            if(b & 1)  
            {  
                ans = ans * a % m;  
                b--;  
            }  
            b >>= 1;  
            a = a * a % m;  
        }  
        return ans;  
    }  
      
    LL C(LL n,LL k,int cur)  
    {  
        LL p = m[cur];  
        if(k > n) return 0;  
        return fac[cur][n] * quick_mod(fac[cur][k] * fac[cur][n-k], p - 2, p) % p;  
    }  
      
    LL Lucas(LL n,LL k,int cur)  
    {  
        LL p = m[cur];  
        if(k == 0)  return 1;  
        return C(n % p, k % p, cur) * Lucas(n / p, k / p, cur) % p;  
    }  
      
    void extend_Euclid(LL a, LL b, LL &x, LL &y)  
    {  
        if(b == 0)  
        {  
            x = 1;  
            y = 0;  
            return;  
        }  
        extend_Euclid(b, a % b,x, y);  
        LL tmp = x;  
        x = y;  
        y = tmp - a / b * y;  
    }  
      
    LL RemindChina(LL a[],LL m[],int k)  
    {  
        LL M = 1;  
        LL ans = 0;  
        for(int i=0; i<k; i++)  
            M *= m[i];  
        for(int i=0; i<k; i++)  
        {  
            LL x, y;  
            LL Mi = M / m[i];  
            extend_Euclid(Mi, m[i], x, y);  
            ans = (ans + Mi * x * a[i]) % M;  
        }  
        if(ans < 0)  
            ans += M;  
        return ans;  
    }  
      
    int main()  
    {  
        Init();  
        while(cin>>N>>G)  
        {  
            a[0] = a[1] = 0;  
            a[2] = a[3] = 0;  
            if(G == P)  
            {  
                cout<<"0"<<endl;  
                continue;  
            }  
            G %= P;  
            for(int i=1; i*i <= N; i++)  
            {  
                if(N % i == 0)  
                {  
                    LL x = i;  
                    a[0] = (a[0] + Lucas(N, x, 0)) % m[0];  
                    a[1] = (a[1] + Lucas(N, x, 1)) % m[1];  
                    a[2] = (a[2] + Lucas(N, x, 2)) % m[2];  
                    a[3] = (a[3] + Lucas(N, x, 3)) % m[3];  
                    x = N / i;  
                    if(i * i != N)  
                    {  
                        a[0] = (a[0] + Lucas(N, x, 0)) % m[0];  
                        a[1] = (a[1] + Lucas(N, x, 1)) % m[1];  
                        a[2] = (a[2] + Lucas(N, x, 2)) % m[2];  
                        a[3] = (a[3] + Lucas(N, x, 3)) % m[3];  
                    }  
                }  
            }  
            LL ans = quick_mod(G, RemindChina(a, m, 4), P);  
            cout<<ans<<endl;  
        }  
        return 0;  
    }  

    题目:已知有如下表达式

     

         

     

          给定,求

     

    分析:如果直接二项式展开,这样会很麻烦,而且不容易求出,本题有技巧。做如下变换

     

         

      

         所以问题变为求的值。 















    展开全文
  • 这个问题也比较常见的,在面试题或者工作中都有类似情况。
  • 组合数求模

    2014-10-19 23:41:05
    组合数取模在ACM竞赛中是一个很重要的问题,很多选手因为数据太大而束手无策,今天就来详细讲解它。... 这个问题比较简单,组合数的计算可以靠杨辉三角,那么由于和的范围小,直接两层循环即可。   (2)
  • 常用数字处理算法 Verilog 实现 1加法器Verilog 实现 串行加法器 组合逻辑加法器可以利用真表通过与门和非门简单地实现假设 和 表示 两个加 表示和 表示来自低位进位 表示向高位进位每个全加器都 执行...
  • 转载ACM组合数取模

    2018-07-17 18:06:23
    组合数取模就是求的值,当然根据,和的取值范围不同,采取的方法也不一样。 &amp;nbsp; 接下来,我们来学习一些常见的取值情况 &amp;nbsp; (1)和 &amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nb
  • 【转】组合数取模

    2017-01-17 14:59:00
    接下来,我们来学习一些常见的取值情况 (1)和 这个问题比较简单,组合数的计算可以靠杨辉三角,那么由于和的范围小,直接两层循环即可。 (2)和,并且是素数 这个问题有个叫做Lucas的定理,定理描述是,如果 ...
  • 组合数取模(数论)

    2015-08-07 09:44:41
    组合数取模在ACM竞赛中是一个很重要的问题,很多选手因为数据太大而束手无策,今天就来详细讲解它。... 这个问题比较简单,组合数的计算可以靠杨辉三角,那么由于和的范围小,直接两层循环即可。   (2)
  • 组合数取余(转)

    千次阅读 2014-11-20 18:18:21
    组合数取模 分类: 数论 2012-10-03 12:41 ...组合数取模就是求的值,当然根据,和的取值范围不同,采取的方法也不一样。   接下来,我们来学习一些常见的取值情况   (1)和    这个问
  • 组合数取模在ACM竞赛中是一个很重要的问题,很多选手因为数据太大而束手无策,今天就来详细讲解它。... 这个问题比较简单,组合数的计算可以靠杨辉三角,那么由于和的范围小,直接两层循环即可。   (2)
  • 组合数取模在ACM竞赛中是一个很重要的问题,很多选手因为数据太大而束手无策,今天就来详细讲解它。   组合数取模就是求的值,当然... 这个问题比较简单,组合数的计算可以靠杨辉三角,那么由于和的范围小,
  • 【数论】组合数取模

    2014-09-19 09:57:28
    分类: 数论 2012-10-03 12:41 2440人阅读 评论...组合数取模就是求的值,当然根据,和的取值范围不同,采取的方法也不一样。   接下来,我们来学习一些常见的取值情况   (1)和    这个
  • 组合数取模问题【数论】

    千次阅读 2014-08-26 16:49:29
    组合数取模在ACM竞赛中是一个很重要的问题,很多选手因为数据太...组合数取模就是求的值,当然根据,和的取值范围不同,采取的方法也不一样。   接下来,我们来学习一些常见的取值情况   (1)和
  • 该表是ref,字段id为refid cellid cat,其中包含整数查询1:查找每个唯一cellid总行SELECT cellid,COUNT(*) totalcount,cat FROM rel GROUP BY cellid查询2:为每个唯一cellid查找catMode(最常见值)SELECT ...
  • [面试常见题系列]:输出1,2,3,..,n所有组合数 思路:递归算法 从开始往后递增地写数字,当前从now开始,存储位置从cur开始, 则显然加上,now..n,都是新的组合数,对于每一个,{ 输出之,然后递归,处理 _...
  • 此时较简单,在O(n2)可承受的情况下组合数的计算可以直接用杨辉三角递推,边做加法边取模。 (2) , ,并且是素数  本文针对该取值范围较大又不太大的情况(2)进行讨论。 这个问题可以使用Lucas定理,定理.....
  • 该表是ref,字段id为refid cellid cat,其中包含整数查询1:查找每个唯一cellid总行SELECT cellid, COUNT(*) totalcount, cat FROM rel GROUP BY cellid查询2:为每个唯一cellid查找catMode(最常见值)SELECT ...
  • 数字前端常见笔试题

    2020-04-03 09:03:49
    组合逻辑中,门电路输入信号由于经过路径不同,到达该门时间不同,叫做竞争 由于竞争而在输出时可能产生尖峰毛刺或脉冲叫做冒险(静态冒险和动态冒险) 1->0->1(静态1冒险) ; 0->1->0(静态0冒险...
  • 题目描述:给定一个有n个数字数组,给出一个数字m,要求所有和等于m的组合。 思路:从头遍历,查找当前这个在路径中时能不能和后面的数构成和,如果可以就输出这个路径,如果加上这个比和sum大,说明不能有...
  • 本文主要总结在一个数组中取出两个,这两个满足条件为:两之和为制定目标target,并且函数返回这两个下表。...根据题意,可以看出这是一个组合问题,也就是高中数学常见的排列组合基本问题。 ...

空空如也

空空如也

1 2 3 4 5 ... 14
收藏数 262
精华内容 104
关键字:

常见组合数的值