lucas定理用于求大组合数模小质数的值
公式 C(n, m) % p = C(n / p, m / p) * C(n%p, m%p) % p
如果n/p还是很大,就可以继续递归下去。
而C(n%p,m%p)可以直接根据C(n,m)=n!/(m!(n-m)!)来算;先存好12*…p的值然后算出(m!(n-m)!)对于p的逆元。就可以算出C(n,m)的值了
对于求小于5000以内的组合数模数可以直接用C(n,m)=C(n-1,m)+C(n-1,m-1)来存;
代码:
void CC(ll n,ll mod)
{
for(int i=0;i<n;i++)
{
C[i][0]=C[i][i]=1;
}
for(int i=1;i<n;i++)
{
for(int j=1;j<=i;j++)
{
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
//sum[i][j]=C[i][j]+sum[i][j-1];
}
}
}在这里插入代码片
lucas实现代码
代码:
ll lucas(ll a,ll b){
if(a<b)return 0;
else if(b==0)return 1;
else if(a<mod&&b<mod)return C[a][b];
else return C[a%mod][b%mod]*lucas(a/mod,b/mod)%mod;
}在这里插入代码片
知道了大组合数求模公式后,可以进而得出求大组合数任意前缀和求模,
设num[n][m]=C(n,0)+C(n,1)+…+C(n,m);
可以先存小于n<mod&&m<mod的num值
代码:
for(int i=0;i<mod;i++)
{
for(int j=0;j<mod;j++)
sum[i][j]=(C[i][j]+sum[i][j-1])%mod;
}在这里插入代码片
推导:
其中要结合lucas定理实现代码:
代码:
ll ca(ll n,ll m)
{
if(n<mod) return sum[n][m];
return (sum[n%mod][n%mod]%mod*ca(n/mod,m/mod-1)%mod+lucas(n/mod,m/mod)*sum[n%mod][m%mod])%mod;
}在这里插入代码片
以一道例题为例:
牛客acm训练题组
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=2333;
ll C[2338][2338];
ll sum[2338][2338];
void CC()
{
for(int i=0;i<mod;i++)
{
C[i][0]=C[i][i]=1;
}
for(int i=1;i<mod;i++)
{
for(int j=1;j<=i;j++)
{
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
//sum[i][j]=C[i][j]+sum[i][j-1];
}
}
for(int i=0;i<mod;i++)
{
for(int j=0;j<mod;j++)
sum[i][j]=(C[i][j]+sum[i][j-1])%mod;
}
}
ll comp(ll a,ll b){
return C[a][b];
}
ll lucas(ll a,ll b){
if(a<b)return 0;
else if(b==0)return 1;
else if(a<mod&&b<mod)return C[a][b];
else return C[a%mod][b%mod]*lucas(a/mod,b/mod)%mod;
}
ll ca(ll n,ll m)
{
if(n<mod) return sum[n][m];
return (sum[n%mod][n%mod]%mod*ca(n/mod,m/mod-1)%mod+lucas(n/mod,m/mod)*sum[n%mod][m%mod])%mod;
}
int main()
{
ll a,b;
int n;
CC();
scanf("%d",&n);
while(n--){
scanf("%lld%lld",&a,&b);
ll sum=0;
printf("%lld\n",ca(a,b));
}
return 0;
}在这里插入代码片
求特定组合数求模的办法
:
代码:
int Calc(int n,int m)
{
return (LL)jc[n]*ny[m]%MOD*ny[n-m]%MOD;
}
for (int i=2;i<=30000;i++)
jc[i]=(LL)jc[i-1]*i%MOD,ny[i]=(LL)(MOD-MOD/i)*ny[MOD%i]%MOD;
for (int i=2;i<=30000;i++) ny[i]=(LL)ny[i-1]*ny[i]%MOD;
在这里插入代码片