lucas 定理学习

时间:2023-03-09 04:08:26
lucas 定理学习

lucas 定理学习

大致意思就是求组合数C(n , m) % p的值, p为一个偶数

可以将组合数的n 和 m都理解为 p 进制的表示

n  = ak*p^k + a(k-1)*p^(k-1) + ... + a1*p + a0

m = bk*p^k + b(k-1)*p^(k-1) + ... + b1*p + b0

然后C(n,m)%p = C(ak , bk) * C(a(k-1) , b(k-1)) * ... * C(a1 , b1) * C(a0 , b0) % p

当然这其中出现 ai < bi的情况那直接视为乘以了 0

其他情况都是正常的组合数计算

因为p为素数,取模的过程求逆元就是利用欧拉定理来求解

a^(-1) = a^(p-2) (mod p)

那么只要快速幂求a^(p-2) % p的值就行了 , 那么组合数C(ai , bi) 就可以算出来了

HDU 4349 求C(n , i)中 0<=i<=n 中多少个可以使C(n , i)为奇数

这里先将n转化为二进制表示,因为C(n,m)%p = C(ak , bk) * C(a(k-1) , b(k-1)) * ... * C(a1 , b1) * C(a0 , b0) % p

那么只会出现ai = 0 , 1 bi = 0 , 1的情况

那么只有ai=0 , bi = 1 才是C(ai , bi) = 0为偶数,其他时候都是奇数,那只要枚举每一位保证那一位出现的数字可能不超过n对应的二进制位即可

 #include <bits/stdc++.h>
using namespace std; int main() {
int n;
while(~scanf("%d" , &n)){
int ret = ;
while(n){
ret = ret*((n&)+);
n>>=;
}
printf("%d\n",ret);
}
return ;
}

HDU 3037 一道比较裸的lucas定理的题目

求C(n+m , n)%p的值

 #include <bits/stdc++.h>
using namespace std;
#define ll long long int q_pow(int a , int b , int p)
{
ll ret = ;
while(b){
if(b&) ret = ret*a%p;
a = (ll)a*a%p;
b>>=;
}
return ret;
} int C(int a , int b , int p)
{
if(b==) return ;
if(a<b) return ;
if(a==b) return ;
int s= , t=;
for(int i= ; i<=b ; i++) s=(ll)s*(a-i+)%p;
for(int i= ; i<=b ; i++) t=(ll)t*i%p;
//cout<<"C: "<<a<<" "<<b<<" "<<p<<" "<<s<<" "<<t<<endl;
return (ll)s*q_pow(t , p- , p)%p;
} int lucas(int a, int b,int p)
{
//cout<<"in: "<<a<<" "<<b<<" "<<p<<endl;
if(b==) return ;
if(a<b) return ;
if(a==b) return ;
//cout<<"en: "<<C(a%p , b%p , p)<<endl;
return (ll)C(a%p , b%p , p)*lucas(a/p , b/p , p)%p;
} int main() {
//freopen("in.txt" , "r" , stdin);
int n;
scanf("%d" , &n);
while(n--){
int a , b , p;
scanf("%d%d%d", &a , &b , &p);
printf("%d\n" , lucas(a+b,a,p));
}
return ;
}