zoukankan      html  css  js  c++  java
  • Problem 2020 组合(FOJ)

    Problem 2020 组合

    Accept: 714    Submit: 1724
    Time Limit: 1000 mSec    Memory Limit : 32768 KB

     Problem Description

    给出组合数C(n,m), 表示从n个元素中选出m个元素的方案数。例如C(5,2) = 10, C(4,2) = 6.可是当n,m比较大的时候,C(n,m)很大!于是xiaobo希望你输出 C(n,m) mod p的值!

     Input

    输入数据第一行是一个正整数T,表示数据组数 (T <= 100) 接下来是T组数据,每组数据有3个正整数 n, m, p (1 <= m <= n <= 10^9, m <= 10^4, m < p < 10^9, p是素数)

     Output

    对于每组数据,输出一个正整数,表示C(n,m) mod p的结果。

     Sample Input

    2 5 2 3 5 2 61

     Sample Output

    1 10

     Source

    FOJ有奖月赛-2011年04月(校赛热身赛)

        ,并且是素数

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

         

         那么得到

          

    即C(n,m)模p等于p进制数上各位的C(ni,mi)模p的乘积。利用该定理,可以将计算较大的C(n,m)转化成计算各个较小的C(ni,mi)。
    该方案能支持整型范围内所有数的组合数计算,甚至支持64位整数,注意中途溢出处理。该算法的时间复杂度跟n几乎不相关了,可以认为算法复杂度在常数和对数之间。

    【卢卡斯(Lucas)定理】

    Lucas定理用来求C(a,b)mod p的值,其中p为素数。

    数学表达式为:

    Lucas(a,b,q)=C(a%q,b%q)*Lucas(a/p,b/p,p);

    Lucas(a,0,q)=0;

     

    通过这个定理就可以很方便的把大数的组合转化成小数。但其中还是要求C(a%q,b%q)%p,所以这里引入逆元来求。

    【定义】若整数a,b,p, 满足a·b≡1(mod p).则称a 为b 模p 的乘法逆元, 即a=b- 1mod p.其中, p 是模数。

    应用到组合数中来就是:

     a!/[b!*(a-b)!] % p == a! * [b!*(a-b)!]-1 %p

    【逆元求法】:

    对于正整数,如果有,那么把这个同余方程中的最小正整数解叫做的逆元。

    逆元一般用扩展欧几里得算法来求得,如果为素数,那么还可以根据费马小定理得到逆元为

    应用费马小定理,ap-1=1 mod p ,即  a*ap-2=1 mod p

    也就是说  ap-2就是a的逆元。

    当然这里求出来的逆元是在取模p的逆元,对我们最终目标没有影响。这也是比较方便而且比较好的方法。

    #include <cstdio>
    #include <iostream>
    #include <sstream>
    #include <cmath>
    #include <cstring>
    #include <cstdlib>
    #include <string>
    #include <vector>
    #include <map>
    #include <set>
    #include <queue>
    #include <stack>
    #include <algorithm>
    using namespace std;
    #define ll long long
    #define _cle(m, a) memset(m, a, sizeof(m))
    #define repu(i, a, b) for(int i = a; i < b; i++)
    #define repd(i, a, b) for(int i = b; i >= a; i--)
    #define sfi(n) scanf("%d", &n)
    #define sfl(n) scanf("%I64d", &n)
    #define pfi(n) printf("%d
    ", n)
    #define pfl(n) printf("%I64d
    ", n)
    #define MAXN 1000005
    
    
    ll quickpow(ll m, ll n , ll k){
        ll   ans = 1;
        while(n){
            if(n & 1)//如果n是奇数
                ans = (ans * m) % k;
            n = n >> 1;//位运算“右移1类似除2”
            m = (m * m) % k;
        }
        return ans;
    }
    
    //ll quickpow(ll a, ll b, ll p)
    //{
    //    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, ll p)
    {
        if(m > n) return 0;
        ll ans = 1;
        for(int i = 1; i <= m; i++)
        {
            ll a = (n - m + i) % p;
            ll b = i % p;
            ans = ans * (a * quickpow(b, p - 2, p) % p) % p;
        }
        return ans;
    }
    
    ll Lucas(ll n, ll m, ll p)
    {
        if(m == 0) return 1;
        else
            return (C(n % p, m % p, p) * Lucas(n / p, m / p, p)) % p;
    }
    int main()
    {
        int T;
        sfi(T);
        while(T--)
        {
            ll n, m, p;
            sfl(n), sfl(m), sfl(p);
            pfl(Lucas(n, m, p));
        }
        return 0;
    }
    View Code
  • 相关阅读:
    【Python】多态、协议和鸭子类型
    【Python】魔法方法之__call__,将对象当方法使用
    【Python】策略模式
    【Python】if 后怎么就可以跟对象?变量交换写法是语法糖吗?
    【Python 库】NumPy 超详细教程(3):ndarray 的内部机理及高级迭代
    【Python 库】NumPy 超详细教程(2):数据类型
    【Python 库】NumPy 超详细教程(1):NumPy 数组
    【杂谈】10 年三线小城 IT 开发的感悟
    【PostgreSQL】安装及中文显示
    【Python 库】轻量级 ORM 框架 peewee 用法详解之——增删改查
  • 原文地址:https://www.cnblogs.com/sunus/p/4722935.html
Copyright © 2011-2022 走看看