组合数取模就是求的值,当然根据
,
和
的取值范围不同,采取的方法也不一样。
(1)和
这个问题比较简单,组合数的计算可以靠杨辉三角,那么由于和
的范围小,直接两层循环即可。
(2)和
,并且
是素数
这个问题有个叫做Lucas的定理,定理描述是,如果
那么得到
这样然后分别求,采用逆元计算(除去m!的时候)即可。
(3)和
,并且
可能为合数
这样的话先采取暴力分解,然后快速幂即可。
http://acm.fzu.edu.cn/problem.php?pid=2020
求,其中
,并且
是素数。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 1000000 + 10;
#define INF 0x7fffffff
#define clr(x,y) memset(x,y,sizeof(x))
typedef long long ll;
#define eps 10e-10
const ll Mod = 1000000007;
ll pow_mod(ll x,ll n,ll mod_val)
{
ll ans = 1;
ll t = x % mod_val;
while(n)
{
if(n & 1)
ans = ans * t % mod_val;
n >>= 1;
t = t * t % mod_val;
}
return ans;
}
ll C(ll n,ll m,ll p)
{
if(n < m)
return 0;
ll ans = 1;
for(ll i = 1; i <= m; i ++)
{
ans = ans * (n + 1 - i) % p;
ll x = pow_mod(i%p,p - 2,p);
ans = ans * x % p;
}
return ans;
}
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);
}
ll n,m,p;
int main()
{
int Tcase;
scanf("%d",&Tcase);
while(Tcase --)
// while( ~ scanf("%I64d%I64d%I64d",&n,&m,&p))
{
scanf("%I64d%I64d%I64d",&n,&m,&p);
printf("%I64d\n",Lucas(n,m,p));
}
return 0;
}
hdu4349
本题为Lucas定理推导题,我们分析一下 C(n,m)%2,那么由lucas定理,我们可以写
* 成二进制的形式观察,比如 n=1001101,m是从000000到1001101的枚举,我们知道在该定理中
* C(0,1)=0,因此如果n=1001101的0对应位置的m二进制位为1那么C(n,m) % 2==0,因此m对应n为0的
* 位置只能填0,而1的位置填0,填1都是1(C(1,0)=C(1,1)=1),不影响结果为奇数,并且保证不会
* 出n的范围,因此所有的情况即是n中1位置对应m位置0,1的枚举,那么结果很明显就是:2^(n中1的个数)
如果,那么
为奇数,否则为偶数
当然本题要判断的组合数很多,所以不能用上述结论,只能另辟蹊径
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 1000000 + 10;
#define INF 0x7fffffff
#define clr(x,y) memset(x,y,sizeof(x))
typedef long long ll;
#define eps 10e-10
const ll Mod = 1000000007;
int n;
int main()
{
while( ~ scanf("%d",&n))
{
int cnt = 0;
while(n)
{
if(n & 1)
cnt ++;
n >>= 1;
}
printf("%d\n",1 << cnt);
}
return 0;
}
当数据范围为10^5的时候,并且p为合数的时候,直接对c(n,m)进行素数拆分;
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 1000000 + 10;
#define INF 0x7fffffff
#define clr(x,y) memset(x,y,sizeof(x))
typedef long long ll;
#define eps 10e-10
const ll Mod = 1000000007;
ll pow_mod(ll x,ll n,ll mod_val)
{
ll ans = 1;
ll t = x % mod_val;
while(n)
{
if(n & 1)
ans = ans * t % mod_val;
n >>= 1;
t = t * t % mod_val;
}
return ans;
}
ll C(ll n,ll m,ll p)
{
if(n < m)
return 0;
ll ans = 1;
for(ll i = 1; i <= m; i ++)
{
ans = ans * (n + 1 - i) % p;
ll x = pow_mod(i%p,p - 2,p);
ans = ans * x % p;
}
return ans;
}
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);
}
ll is_[maxn];
ll cnt = 0;
ll prime[maxn];
void Init()
{
clr(is_,true);
for(ll i = 2; i < maxn; i ++)
{
if(is_[i])
{
for(ll j = i * 2; j < maxn ; j += i)
is_[j] = false;
prime[cnt ++] = i;
}
}
}
//n!中素数的个数
int get_num(ll n,ll p)
{
int ans = 0;
while(n)
{
ans += n / p;
n /= p;
}
return ans;
}
ll Work(ll n,ll m,ll p)
{
ll ans = 1;
for(ll i = 0; i < cnt && prime[i] <= n; i ++)
{
cout << prime[i] << " " << get_num(n,prime[i]) - get_num(n - m,prime[i]) - get_num(m,prime[i]) <<endl;
ans = ans * pow_mod(prime[i],get_num(n,prime[i]) - get_num(n - m,prime[i]) - get_num(m,prime[i]),p) % p;
}
return ans%p;
}
ll n,m,p;
int main()
{
Init();
int Tcase;
scanf("%d",&Tcase);
while(Tcase --)
// while( ~ scanf("%I64d%I64d%I64d",&n,&m,&p))
{
scanf("%I64d%I64d%I64d",&m,&n,&p);
printf("%I64d\n",Work(n,m,p));
}
return 0;
}