求n!中有多少个质因子p
10!中质因子2的个数:
10! | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
10!中有因子21的数 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 |
10!中有因子22的数 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 0 |
10!中有因子23的数 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
因此10!中质因子2的个数有5+2+1=8个。
仔细思考,n!中有(n/p + n/p2 + …… )个这个质因子p,其中除法均为向下取整。
O(logn)
//计算n!中有多少个质因子p
int cal(int n,int p){
int ans = 0;
while(n){
ans += n/p;
n /= p;
}
return ans;
}
求n!中有多少个因子
由上面可以得出,遍历一遍<=n的质数,依次求解该质因子个数c[i],最后 *(c[i]+1)即可
int pri[maxn],tot; //maxn>n即可
bool v[maxn];
int c[maxn];
void primes() //筛素数
{
memset(v,true,sizeof(v));
tot = 0;
for(int i=2;i<maxn;i++){
if(v[i]){
pri[++tot] = i;
}
for(int j=1;j<=tot&&i*pri[j]<maxn;j++){
v[i*pri[j]] = 0;
if(i%pri[j]==0) break;
}
}
}
void cal(int n)
{
int p,ans;
for(int i=1;i<=tot;i++){
p = pri[i];ans = 0;
while(n/p){
ans += n/p;
p *= pri[i];
}
c[i] += ans;
}
}
ll ans = 1;
for(int i=1;i<=tot;i++)
ans *= (c[i]+1);
拓展: C(n,m)的因子个数:
C(n,m) = n ! m ! ( n − m ) ! \frac{n!}{m!(n-m)!} m!(n−m)!n!。
方法一:
暴力解决:依次求解n!每个质因子的个数a[i],m!每个质因子的个数b[i],(n-m)!每个质因子的个数c[i],*(a[i]-b[i]-c[i]+1);
int pri[maxn],tot;
bool v[maxn];
int c[maxn];
void primes()
{
memset(v,true,sizeof(v));
tot = 0;
for(int i=2;i<maxn;i++){
if(v[i]){
pri[++tot] = i;
}
for(int j=1;j<=tot&&i*pri[j]<maxn;j++){
v[i*pri[j]] = 0;
if(i%pri[j]==0) break;
}
}
}
void cal(int n,int d)
{
int p,ans;
for(int i=1;i<=tot;i++){
p = pri[i];ans = 0;
while(n/p){
ans += n/p;
p *= pri[i];
}
c[i] += d*ans;
}
}
cal(n,1); //n!每个质因子的个数
cal(m,-1); //m!每个质因子的个数
cal(n-m,-1); //(n-m)!每个质因子的个数
ll ans = 1;
for(int i=1;i<=tot;i++)
ans *= (c[i]+1); //因子个数
打表
j!中每个素因子的个数,公式如下:num[j][i] = j/pri[i] + num[j/pri[i]][i];
int pri[maxn],tot;
bool v[maxn];
int num[maxn][maxn];
ll c[maxn][maxn];
void solve()
{
memset(v,true,sizeof(v));
tot = 0;
for(int i=2;i<maxn;i++){
if(v[i]){
pri[++tot] = i;
}
for(int j=1;j<=tot&&i*pri[j]<maxn;j++){
v[i*pri[j]] = 0;
if(i%pri[j]==0) break;
}
}
for(int i=1;i<=tot;i++){
for(int j=1;j<maxn;j++){
num[j][i] = j/pri[i] + num[j/pri[i]][i];
}
}
for(int i=1;i<maxn;i++){
for(int j=1;j<i;j++){
c[i][j] = 1;
for(int k=1;k<=tot;k++){
int d = num[i][k] - num[j][k] - num[i-j][k];
c[i][j] *= (d+1);
}
}
}
}
if(m==0||n==m)
printf("1\n");
else
printf("%lld\n",c[n][m]);
例题:POJ 2992 Divisors http://poj.org/problem?id=2992
Description
Your task in this problem is to determine the number of divisors of Cnk. Just for fun – or do you need any special reason for such a useful computation?
Input
The input consists of several instances. Each instance consists of a single line containing two integers n and k (0 ≤ k ≤ n ≤ 431), separated by a single space.
Output
For each instance, output a line containing exactly one integer – the number of distinct divisors of Cnk. For the input instances, this number does not exceed 263 - 1.
Sample Input
5 1
6 3
10 4
Sample Output
2
6
16
题意
求解C(n,m)的因子个数。
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<ctime>
#define ll long long
#define ld long double
#define ull unsigned long long
using namespace std;
const int maxn = 440;
const int inf = 0x3f3f3f3f3f;
const ll lnf = 0x3f3f3f3f3f3f3f;
const ll mod = 1000000007;
int pri[maxn],tot;
bool v[maxn];
int c[maxn];
void primes()
{
memset(v,true,sizeof(v));
tot = 0;
for(int i=2;i<maxn;i++){
if(v[i]){
pri[++tot] = i;
}
for(int j=1;j<=tot&&i*pri[j]<maxn;j++){
v[i*pri[j]] = 0;
if(i%pri[j]==0) break;
}
}
}
void cal(int n,int d)
{
int p,ans;
for(int i=1;i<=tot;i++){
p = pri[i];ans = 0;
while(n/p){
ans += n/p;
p *= pri[i];
}
c[i] += d*ans;
}
}
int main(void)
{
int k,n;
primes();
while(~scanf("%d%d",&n,&k)){
memset(c,0,sizeof(c));
cal(n,1);cal(k,-1);
cal(n-k,-1);
ll ans = 1;
for(int i=1;i<=tot;i++)
ans *= (c[i]+1);
printf("%lld\n",ans);
}
return 0;
}
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<ctime>
#define ll long long
#define ld long double
#define ull unsigned long long
using namespace std;
const int maxn = 440;
const int inf = 0x3f3f3f3f3f;
const ll lnf = 0x3f3f3f3f3f3f3f;
const ll mod = 1000000007;
int pri[maxn],tot;
bool v[maxn];
int num[maxn][maxn];
ll c[maxn][maxn];
void solve()
{
memset(v,true,sizeof(v));
tot = 0;
for(int i=2;i<maxn;i++){
if(v[i]){
pri[++tot] = i;
}
for(int j=1;j<=tot&&i*pri[j]<maxn;j++){
v[i*pri[j]] = 0;
if(i%pri[j]==0) break;
}
}
for(int i=1;i<=tot;i++){
for(int j=1;j<maxn;j++){
num[j][i] = j/pri[i] + num[j/pri[i]][i];
}
}
for(int i=1;i<maxn;i++){
for(int j=1;j<i;j++){
c[i][j] = 1;
for(int k=1;k<=tot;k++){
int d = num[i][k] - num[j][k] - num[i-j][k];
c[i][j] *= (d+1);
}
}
}
}
int main(void)
{
int k,n;
solve();
while(~scanf("%d%d",&n,&k)){
if(k==0||n==k) printf("1\n");
else printf("%lld\n",c[n][k]);
}
return 0;
}