先挖个坑,整理了一下算法笔记中的思路,具体代码之后补上,点击跳转这个链接里有po主码的算法笔记里的代码,本文中引用了里面的部分内容
组合式定义:
性质:
1.C(n,m)=C(n,n-m)
2.C(n,n)=C(0,0)=1
方法一:通过定义式直接计算
typedef long long int LL;
LL C(LL n, LL m)
{
LL ans = 1;
for(LL i=1; i<=n; i++)
ans*=i;
for(LL i=1; i<=m; i++)
ans/=i;
for(LL i=1; i<=n-m; i++)
ans/=i;
return ans;
}
方法二:通过递推公式计算
C(n,m)=C(n-1,m-1)+C(n-1,m)
LL C(LL n, LL m)
{
if(m==0||m==n) return 1;
return C(n-1, m) + C(n-1, m-1);
}
优化:通过表记录,避免重复运算
LL res[67][67] ={0};
LL C(LL n, LL m)
{
if(m==0||m==n)
return 1;
if(res[n][m]!=0) return res[n][m];
return res[n][m] = C(n-1,m)+C(n-1, m-1);
}
或,提前计算出整张表
const int n=60;
void calC()
{
for(int i=0; i<=n; i++)
res[i][0] =res[i][i]=1;//初始化边界
for(int i=2; i<=n; i++)
for(int j=0; j<=i/2; j++)
{
res[i][j] = res[i-1][j] +res[i-1][j-1];
res[i][i-j] = res[i][j]; //C(i,i-j) = C(i,j)
}
}
方法三:通过定义式的变形
LL C(LL n, LL m)
{
LL ans = 1;
for(LL i=1; i<=m; i++)
ans = ans*(n-m+i)/i;//注意一定要先乘再除
return ans;
}
那么如果要计算C(n,m)%p呢?
方法一:根据递推公式
LL res[1010][1010] ={0};
LL C(LL n, LL m, int p)
{
if(m==0||m==n)
return 1;
if(res[n][m]!=0) return res[n][m];
return res[n][m] = C(n-1,m)+C(n-1, m-1) % p;
}
或
int n;
void calC()
{
for(int i=0; i<=n; i++)//初始化边界
res[i][0] = res[i][i]=1;
for(int i=2; i<=n; i++)
for(int j=0; j<=i/2; j++)
{
res[i][j] = (res[i-1][j] + res[i-1][j-1]) % p ;
res[i][i-j] = res[i][j]; //C(i,i-j) = C(i,j)
}
}
方法二:根据定义式
int prime[maxn];//使用筛法得到的素数表
int C(int n, int m, int p)
{
for(int i=0; prime[i]<=n; i++)
{
//计算C(n,m)中prime[i]的指数c, cal(n,k)为n!中含质因子k的个数
int c = cal(n, prime[i]) - cal(m,prime) - cal(n-m, prime);
//快速幂计算prime^c%p;
ans = ans *binaryPow(prime[i], c, p)%p;
}
return ans;
}
方法三:根据定义式的变形*
方法四:Lucas定理
扫描二维码关注公众号,回复:
6013622 查看本文章
int Lucas(int n, int m)
{
if(m==1) return 1;
return C(n%p, m%p) * Lucas(n/p, m/p)%p;
}