【卢卡斯定理】
卢卡斯定理是用来求解: Cmn+m C n + m m %mod的值,而mod是一个质数。
我也是看了很长时间的讲解才看懂的(感觉好弱啊~~~)
这张图是我认为讲解最全的,(可能一时看不懂,我再补充一下)。
首先,你要看懂
(1+x)p≡1+xp%p
(
1
+
x
)
p
≡
1
+
x
p
%
p
并推出
(1+x)a≡(1+x)a0∗(1+xp)a1∗∙∙∙∗((1+x)pk)ak%p
(
1
+
x
)
a
≡
(
1
+
x
)
a
0
∗
(
1
+
x
p
)
a
1
∗
•
•
•
∗
(
(
1
+
x
)
p
k
)
a
k
%
p
以上都不难,最难的是最后一步,我一直没有想明白,想了很久才明白的(太菜了)。
就是左右两边的 xb x b 的项系数是相同的,
左边是
Cba∗xb
C
a
b
∗
x
b
右边是,每一项提取一个式子
Cb0a0xb0∗Cb1a1xpb1∗Cb2a2xp2b2∗∙∙∙∗Cbkakxpkbk
C
a
0
b
0
x
b
0
∗
C
a
1
b
1
x
p
b
1
∗
C
a
2
b
2
x
p
2
b
2
∗
•
•
•
∗
C
a
k
b
k
x
p
k
b
k
=Cb0a0∗Cb1a1∗Cb2a2∗∙∙∙∗Cbkak∗xb0+pb1+p2b2+∙∙∙+pkbk
=
C
a
0
b
0
∗
C
a
1
b
1
∗
C
a
2
b
2
∗
•
•
•
∗
C
a
k
b
k
∗
x
b
0
+
p
b
1
+
p
2
b
2
+
•
•
•
+
p
k
b
k
而 b0+pb1+p2b2+∙∙∙+pkbk b 0 + p b 1 + p 2 b 2 + • • • + p k b k 是p进制的b,所以是 xb x b 所以
Cba=Cb0a0∗Cb1a1∗Cb2a2∗∙∙∙∗Cbkak
C
a
b
=
C
a
0
b
0
∗
C
a
1
b
1
∗
C
a
2
b
2
∗
•
•
•
∗
C
a
k
b
k
证明完毕了!
也可以用递归的方法来写:Lucas(a,b)=Lucas(n%p,m%p)*Lucas(n/p,m/p)%p;
继续对Lucas(n/p,m/p)递归即可。
【线性求解逆元】
这个博客讲解的很清楚了:线性求解逆元
求解n以内的逆元,时间复杂度O(n)
设 t=M/i , k=M%i;
inv数组记录的就是i的逆元。
下面是一道例题:
卢卡斯定理
代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e5+9;
ll factorial[maxn];//记录阶乘
ll inelement[maxn];//记录,先记录每个数的逆元,再记录阶乘的逆元
ll lucas(int n,int m,int mod)
{
if(n<m)
return 0;
else if(n<mod)
return factorial[n]*inelement[m]*inelement[n-m]%mod;
else return lucas(n%mod,m%mod,mod)*lucas(n/mod,m/mod,mod)%mod;
}
int main()
{
int t,n,m,mod;
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d",&n,&m,&mod);
factorial[0]=factorial[1]=inelement[0]=inelement[1]=1;
//阶乘
for(int i=2;i<=m+n;i++)
{
factorial[i]=factorial[i-1]*i%mod;
}
//逆元,先先求每一个数的逆元,线性求解
for(int i=2;i<=m+n;i++)
{
inelement[i]=(mod-mod/i)*inelement[mod%i]%mod;
}
//求阶乘的逆元
for(int i=2;i<=m+n;i++)
{
inelement[i]=inelement[i-1]*inelement[i]%mod;
}
printf("%lld\n",lucas(n+m,m,mod));
}
return 0;
}