杭电3037(组合 + 逆元 + 快速幂 + 卢卡斯)
http://acm.hdu.edu.cn/showproblem.php?pid=3037
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int Max = 100100;
__int64 aa[Max], p;
void fn() //枚举 n!
{
aa[0] = 1;
for(int i = 1; i <= p; i++)
aa[i] = aa[i - 1] * i % p;
}
__int64 Pow(__int64 a, __int64 b) //快速幂计算a^b
{
__int64 sum = 1;
while(b > 0)
{
if(b & 1)
sum = sum * a % p;
a = a * a % p;
b >>= 1;
}
return sum;
}
__int64 _lukasi(__int64 a, __int64 b) //lukasi定理
{
__int64 res = 1;
while(a && b)
{
__int64 x = a % p, y = b % p;
if(x < y)
return 0;
res = res * aa[x] * Pow(aa[y] * aa[x - y] % p, p - 2) % p;
a /= p;
b /= p;
}
return res;
}
int main()
{
int t;
__int64 n, m;
scanf("%d", &t);
while(t--)
{
scanf("%I64d%I64d%I64d", &n, &m, &p);
fn();
printf("%I64d\n", _lukasi(n + m, m));
}
return 0;
}
卢卡斯定理:
设a[x] 为x的阶乘x!
res = a[a % p] * (a[b % p] * a[(a - b) % p], p -2) % p; //1-1
lukasi(a, b, p) = lukasi(a / p, b / p, p) * res; //1-2
当a, b <100000 时,用逆元就可以了,当a, b 很大时, 就要用到lukasi定理了
c(m, n) = n! / ( (n - m)! * m!)
x ^ (p - 2) % p = (x % p) ^ (p - 2) % p //这一步很重要,就是下面 证明的前提
n!单独提出来,每P一个阶段,每个数取模P后, n! = p ^ (n / p) * (n % p) !
同理 m! = p ^ (m / p) * (m % p) !
(n - m)! = p ^ ((n - m) / p) * ((n - m) % p) !
n / p - m / p - (n - m) / p = 0

浙公网安备 33010602011771号