您的位置:首页 > 其它

hdu5446 Unknown Treasure(Lucas+中国剩余定理)

2015-09-18 21:14 435 查看
Problem Description
On the way to the next secret treasure hiding place, the mathematician discovered a cave unknown to the map. The mathematician entered the cave because it is there. Somewhere deep in the cave, she found a treasure chest with a combination
lock and some numbers on it. After quite a research, the mathematician found out that the correct combination to the lock would be obtained by calculating how many ways are there to pick
m


different apples among n


of them and modulo it with M

.
M


is the product of several different primes.

Input
On the first line there is an integer
T(T≤20)


representing the number of test cases.

Each test case starts with three integers n,m,k(1≤m≤n≤10

18

,1≤k≤10)


on a line where k


is the number of primes. Following on the next line are
k


different primes p

1

,...,p

k



.
It is guaranteed that M=p

1

⋅p

2

⋅⋅⋅p

k

≤10

18




and p

i

≤10

5




for every i∈{1,...,k}

.

Output
For each test case output the correct combination on a line.

Sample Input
1
9 5 2
3 5


Sample Output
6


Source
2015 ACM/ICPC Asia Regional Changchun Online

题意:M=p1*p2*...pk;求C(n,m)%M,pi小于10^5,n,m,M都是小于10^18。
分析:Lucas定理求C(n,m)%M,而中国剩余定理刚好解决后面部分。利用Lucas定理求出所有对pi取模的值,然后在用中国剩余定理求解。
Lucas定理不懂的可以戳这里
中国剩余定理不懂的可以戳这里
扩展欧几里德算法的话可以戳这里
Ps. 刚开始学数论,这个题我看了三天博客,才略懂略懂,只能说数论这东西真尼玛难 - -

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
const double eps = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int MOD = 1000000007;
#define ll long long
#define CL(a) memset(a,0,sizeof(a))
const int maxn = 1e5 + 5;

ll fac[maxn]={0,1},inv[maxn]={0,1};

ll pow_mod(ll a, ll n, ll mod)//快速幂取模
{
    ll ret = 1;
    while (n)
    {
        if (n&1)
            ret = ret*a%mod;
        a = a*a%mod;
        n >>= 1;
    }
    return ret;
}

ll Lucas(ll n, ll m, ll mod)//Lucas定理
{
    ll ret=1;
    ll mm=m,nn=n;
    while (mm && nn)
    {
        ll mod_m=mm%mod, mod_n=nn%mod;
        if (mod_n>=mod_m)
            ret = ret*fac[mod_n]%mod*inv[mod_m]%mod*inv[mod_n-mod_m]%mod;
        else
        {
            ret = 0;
            break;
        }
        mm/=mod;
        nn/=mod;
    }
    return ret;
}

void exgcd(ll a, ll b, ll &d, ll &x, ll &y)//扩展欧几里德求逆元
{
    if (b == 0)
        d = a, x = 1, y = 0;
    else
    {
        exgcd(b, a%b, d, y, x);
        y -= x * (a / b);
    }
}

ll china(ll n, ll m[], ll a[])//中国剩余定理求解
{
    ll aa = a[0];
    ll mm = m[0];
    for (int i=0; i<n; i++)
    {
        ll sub = (a[i] - aa);
        ll d, x, y;
        exgcd(mm, m[i], d, x, y);
        if (sub % d) return -1;

        ll new_m = m[i]/d;
        new_m = (sub/d*x%new_m+new_m)%new_m;
        aa = mm*new_m+aa;
        mm = mm*m[i]/d;
    }
    aa = (aa+mm)%mm;
    return aa;
}

int main ()
{
    int cas;
    ll n, m, k, a[15], p[15];
    scanf("%d", &cas);
    while (cas--)
    {
        scanf("%lld%lld%lld",&n,&m,&k);
        for (int i=0; i<k; i++)
        {
            scanf("%lld",&p[i]);
            //init(p[i]);
            for (int j=2; j<p[i]; j++)
                fac[j] = fac[j-1]*j%p[i];
            inv[p[i]-1] = pow_mod(fac[p[i]-1], p[i]-2, p[i]);
            for (int j=p[i]-1; j>0; j--)
                inv[j-1] = inv[j]*j%p[i];

            a[i] = Lucas(n, m, p[i]);
        }
        printf("%lld\n",china(k, p, a));
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: