Lightoj 1067(逆元)

来源:互联网 发布:c语言二分法求方程的根 编辑:程序博客网 时间:2024/05/05 11:24

Given n different objects, you want to take k of them. How many ways to can do it?

For example, say there are 4 items; you want to take 2 of them. So, you can do it 6 ways.

Take 1, 2
Take 1, 3
Take 1, 4
Take 2, 3
Take 2, 4
Take 3, 4

Input
Input starts with an integer T (≤ 2000), denoting the number of test cases.

Each test case contains two integers n (1 ≤ n ≤ 106), k (0 ≤ k ≤ n).

Output
For each case, output the case number and the desired value. Since the result can be very large, you have to print the result modulo 1000003.

Sample Input
3
4 2
5 0
6 4
Sample Output
Case 1: 6
Case 2: 1
Case 3: 15

两种方法可求解,不过感觉大致上是差不多的。。。

题意:数学组合问题改编成费马小定理—>逆元。。。组合 C(n,m) = n! / (m! * (n - m)! )
逆元:由费马小定理a ^ (p-1) = 1 (%p),两边同时除去a——a ^ (p-2) = a^-1 (%p),得a的逆元a^-1 = a ^ (p-2)。
题解:当n 不大于 1e6 时可以考虑打表,先把每个数的介乘打表,再把每个数的逆元打表,然后就出来答案了

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;#define M 1000003#define LL long longLL pre[M], pows[M];void init()//将每个数的介乘打表 {    pre[0] = 1;    for(int i=1; i<M; i++)    {        pre[i] = pre[i-1] * i % M;    }} LL quickpow(LL n, LL m, LL k)//用快速幂求逆元 {    LL res = 1;    while(m)    {        if(m & 1)        {            res = res * n % k;        }        m >>= 1;        n = n * n % k;    }    return res;}void init2()//将每个数的介乘的逆元打表 {    pows[0] = 1;    pows[1] = 1;    for(LL i=2; i<M; i++)    {        pows[i] = quickpow(pre[i], M-2, M);//传入数据是每个数的介乘     }}int main(){    int t;    LL ans, n, m, a, b, c;    scanf("%d", &t);    init();    init2();    for(int ca=1; ca<=t; ca++)    {        scanf("%lld%lld", &n, &m);        a = pre[n];        b = pows[m];        c = pows[n-m];        ans = (a * c % M) * b % M;        printf("Case %d: %lld\n", ca, ans);    }    return 0;}

lucas定理求解。。。。。lucas大模板题
根据百度可知:C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)%p链接在此
然后就可以开始我们构造函数之路了

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;#define LL long long#define M 1000003LL fac[M];void fact()//构造阶乘{    fac[0] = 1;    for(int i=1; i<M; i++)    {        fac[i] = fac[i-1] * i % M;    }}LL quickpow(LL n, LL m, LL k)//快速幂{    LL res = 1;    while(m)    {        if(m & 1)        {            res = res * n % k;        }        m >>= 1;        n = n * n % k;    }    return res;}LL comb(LL n, LL m, LL k)//这个是组合函数{    LL ans = 1;    ans = fac[n] * quickpow(fac[m]*fac[n-m]%k, k-2, k) % k;    return ans;}LL lucas(LL n, LL m, LL k)//这个是lucas{    LL ans = 1;    if(m > n)   return 0;//这个应是显然咯     while(n && m && ans)    {        ans = ans * comb(n%k, m%k, k);        n /= k;        m /= k;    }    /*    return comb(n%k, m%k, k) * lucas(n/k, m/k, k);递归方法     */    return ans;}int main(){    int t;    LL n, m;    fact();    scanf("%d", &t);    for(int ca=1; ca<=t; ca++)    {        scanf("%lld%lld", &n, &m);        printf("Case %d: %lld\n", ca, lucas(n, m, M));    }    return 0;}
2 0