版权声明:本文为博主原创文章,转载请注明出处 https://blog.csdn.net/xunalove/article/details/88301031
一. 关于n!的一个问题
#include<stdio.h>
/*
计算n!中有多少个质因子p
公式: n!中有(n/p + n/p^2 + n/p^3 + n/p^4......)个质因子p
算法的时间复杂度为O(logn)
*/
int cal(int n, int p)
{
int ans = 0;
while(n)
{
ans+=n/p;
n/=p;
}
return ans;
}
int main()
{
//可以计算n!的末尾有多少个0,由于末尾0的个数等于n!中5的个数
//因为2的个数肯定是大于5的,2和5组合就形成了10
printf("%d", cal(10,5));
}
// 2
二. 组合数的计算
#include<stdio.h>
typedef long long int LL;
/*
组合数C(n,m)的计算
递推式 C(n, m) = C(n-1,m) + C(n-1,m-1)
*/
/*方法一:通过定义式直接计算
范围: n<=20
*/
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;
}
/*
方法二:通过递推公式计算 O(n*n)
*/
LL C1(LL n, LL m)
{
if(m==0||m==n) return 1;
return C1(n-1, m) + C1(n-1, m-1);
}
LL ans[67][67] ={0};
LL C2(LL n, LL m)
{
if(m==0||m==n)
return 1;
if(ans[n][m]!=0) return ans[n][m];
return ans[n][m] = C2(n-1,m)+C2(n-1, m-1);
}
int n;
void calC()
{
for(int i=0; i<=n; i++)//初始化边界
ans[i][0] = ans[i][i]=1;
for(int i=2; i<=n; i++)
for(int j=0; j<=i/2; j++)
{
ans[i][j] = ans[i-1][j] + ans[i-1][j-1];
ans[i][i-j] = ans[i][j]; //C(i,i-j) = C(i,j)
}
}
/*
方法三:通过定义式的变形来计算 O(m)
*/
LL C(LL n, LL m)
{
LL ans = 1;
for(LL i=1; i<=m; i++)
ans = ans*(n-m+i)/i;//注意一定要先乘再除
}
int main()
{
//可以计算n!的末尾有多少个0,由于末尾0的个数等于n!中5的个数
//因为2的个数肯定是大于5的,2和5组合就形成了10
}
// 2
#include<stdio.h>
typedef long long int LL;
/*
计算C(n,m) % p
*/
/*
方法一:通过递推公式计算
m <= n <= 1000,且对p的大小和素性没有额外限制
*/
LL ans[67][67] ={0};
LL C2(LL n, LL m, int p)
{
if(m==0||m==n)
return 1;
if(ans[n][m]!=0) return ans[n][m];
return ans[n][m] = C2(n-1,m)+C2(n-1, m-1) % p;
}
int n;
void calC()
{
for(int i=0; i<=n; i++)//初始化边界
ans[i][0] = ans[i][i]=1;
for(int i=2; i<=n; i++)
for(int j=0; j<=i/2; j++)
{
ans[i][j] = (ans[i-1][j] + ans[i-1][j-1]) % p ;
ans[i][i-j] = ans[i][j]; //C(i,i-j) = C(i,j)
}
}
/*
方法二:根据定义式计算
O(Klogn)
m<=n<=10^6
对p的大小和素性没有额外限制
*/
int prime[maxn];//通过筛法得到的素数表
//遍历不超过n的所有质数
int p[n+1] = {0},k=0;
void IsPrime(int n)
{
for(int i=2; i<=n; i++)
{
if(p[i]==0)
{
prime[k++] = i;
for(int j=i+i; j<=n; j++)
p[j] = true;
}
}
}
int binaryPow(int a, int b, int m)//a^b % m
{
int ans=1;
while(b>0)
{
if(b%2==1)
ans = ans*a%,
a = a*a%m;
b/=2;
}
return ans;
}
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;
}
/*
方法三:通过定义式的变形来计算
m<=n<<10^18
p<=10^5且为素数
*/
/*
方法四:Lucas定理
*/
int Lucas(int n, int m)
{
if(m==1) return 1;
return C(n%p, m%p) * Lucas(n/p, m/p)%p;
}