Unknown Treasure---hdu5446(卢卡斯+中国剩余定理)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5446

C(n, m) % (p1*p2*p3*...*pk)的值

其实这个就是中国剩余定理最后算出结果后的最后一步求余

那C(n, m)相当于以前我们需要用中国剩余定理求的值

然而C(n, m)太大,我们只好先算出

C(n, m) % p1 = r1

C(n, m) % p2 = r2

C(n, m) % p3 = r3

.

.

.

C(n, m) % pk = rk

用Lucas,这些r1,r2,r3...rk可以算出来

然后用中国剩余定理求满足num%p[i]=r[i]的最小num即可,num既是所求答案;

 

注意在运算过程中会出现数相乘爆long long,所以要手动写乘法求余;

 

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <string>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <stack>
#include <math.h>

using namespace std;

#define met(a, b) memset(a, b, sizeof(a))
#define N 153
#define INF 0x3f3f3f3f

typedef long long LL;

///快速幂计算a*b%p,因为a*b直接相乘可能会爆LL;
LL Mul(LL a, LL b, LL p)
{
    a = (a+p)%p;
    b = (b+p)%p;
    LL ans = 0;
    while(b)
    {
        if(b%2)
            ans = (ans+a)%p;
        a=(a+a)%p;
        b/=2;
    }
    return ans;
}

///用快速幂求a^b%p;
LL quick_mod(LL a, LL b, LL p)
{
    LL ans = 1;
    a %= p;
    while(b)
    {
        if(b&1)
        {
            ans = ans*a%p;
            b--;
        }
        b = b/2;
        a = a*a%p;
    }
    return ans;
}
///求C(n, m)%p;
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+i-m)%p;
        LL b = i%p;
        ans = ans*(a*quick_mod(b, p-2, p)%p)%p;
    }
    return ans;
}
///卢卡斯用于求C(n, m)%p;
LL Lucas(LL n, LL m, LL p)
{
    if(m == 0)return 1;
    return C(n%p, m%p, p) * Lucas(n/p, m/p, p)%p;
}
///扩展欧几里德 求ax+by = gcd(a, b)中的x和y
void ex_gcd(LL a, LL b, LL &x, LL &y)
{
    if(b == 0)
    {
        x = 1;
        y = 0;
        return;
    }
    ex_gcd(b, a%b, x, y);
    int t = x;
    x = y;
    y = t - a/b*y;
    if( a*b < 0 )///当ab异号时;
    {
        x = -x;
        y = -y;
    }
}
///已知 num%p[i]=r[i];求满足n个式子的最小num,其中p[i]是素数;
LL China(int n, LL p[], LL r[])
{
    LL m = 1, num = 0;
    for(int i=1; i<=n; i++)
        m *= p[i];
    for(int i=1; i<=n; i++)
    {
        LL x, y, a = m/p[i], b = -p[i];
        ex_gcd(a, b, x, y);
        LL t = Mul(a, x, m);///Mul就是乘法,防止爆longlong;
        num = (num + Mul(t, r[i], m) + m) % m;
    }
    return (num+m)%m;
}

int main()
{
    LL n, m;
    int k, T;
    scanf("%d", &T);
    while(T--)
    {
        LL p[15], r[15];
        scanf("%I64d %I64d %d", &n, &m, &k);
        for(int i=1; i<=k; i++)
        {
            scanf("%I64d", &p[i]);
            r[i] = Lucas(n, m, p[i]);
        }
        printf("%I64d\n", China(k, p, r));
    }
    return 0;
}
View Code

 

posted @ 2016-05-20 11:10  西瓜不懂柠檬的酸  Views(156)  Comments(0)    收藏  举报
levels of contents