杭电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

 

posted @ 2016-03-01 20:21  海无泪  阅读(111)  评论(0编辑  收藏  举报