牛客网 阶乘分解 (数学)

题目描述:

给定整数 N(1≤N≤106),试把阶乘 N! 分解质因数,按照算术基本定理的形式输出分解结果中的 P i P_i Pi C i C_i Ci 即可。
输入描述:
一个整数N。
输出描述:
N! 分解质因数后的结果,共若干行,每行一对 P i P_i Pi, C i C_i Ci​,表示含有 P i C i P_i^{C_i} PiCi​​项。按照 P i P_i Pi从小到大的顺序输出。
示例1
输入
5
输出
2 3
3 1
5 1
说明
5!=120=23 *3*5
题目链接:https://ac.nowcoder.com/acm/contest/1021/B
来源:牛客网

80分暴力解法:

1.因为N!=1*2*3*…*(N-1)*N,所以N!的质因子必然小于N。故只需要用线性筛获得[1,N]之间的素数表。不会素数线性筛的看这里->素数线性筛
2.对N的阶乘进行质因数分解转化为对[1,N]的每一个数进行质因数分解再将相同质因子的指数数累加即可。
3.依次用素数表中的质数对[2,N]区间的数i进行“试除”,直到i中不含质因子。

#include<cstdio>
const int maxn=1e6+20;
int vis[maxn];
int primes[maxn];   / /素数表
int counts[maxn];  / /统计素数个数,counts[i]表示primes[i]的个数
void get_primes(int n){  / /线性素数筛获得素数表
int cnt=0;  
for(int i=2;i<=n;i++){
if(vis[i]==0){primes[cnt]=i;cnt++;}   
 for(int j=0;j<cnt;j++){
 if(i*primes[j]>n) break;
 vis[i*primes[j]]=1;
 if(i%primes[j]==0) break;   
 }      
}   
}
int main(){
int N;
scanf("%d",&N);
get_primes(N);
for(int i=2;i<=N;i++){
int t=i;     
int cnt=0;   
while(t>1){ / /注意t为1的时候就没有质因子了 
if(t%primes[cnt]==0){ / /primes[cnt]是当前i(用t先代替)的质因子,则统计次数,并将i除去因子primes[i]
counts[cnt]++;
t=t/primes[cnt];
}
else cnt++; / /primes[cnt]不是当前i 的因子,则换另一个质数试探   
}
}
int i=0; 
while(counts[i]>=1){ / /输出素数及对应的个数
printf("%d %d\n",primes[i],counts[i]);    
i++;  
}   
return 0;   
}

以上代码中的“试除”部分效率过低,无法AC。

正解

其实此处涉及到一个数论知识:

  • N!中质因子p的个数为: ⌊ N p ⌋ \lfloor{\frac{N}{p}}\rfloor pN+ ⌊ N p 2 ⌋ \lfloor{\frac{N}{p^2}}\rfloor p2N+ ⌊ N p 3 ⌋ \lfloor{\frac{N}{p^3}}\rfloor p3N+…+ ⌊ N p ⌊ log ⁡ N P ⌋ ⌋ \lfloor{\frac{N}{p\lfloor{\log_N P}\rfloor}}\rfloor plogNPN。即1~N之间的数中含有因子p的数有 ⌊ N p ⌋ \lfloor{\frac{N}{p}}\rfloor pN个,含有因子p2的数有 ⌊ N p 2 ⌋ \lfloor{\frac{N}{p^2}}\rfloor p2N
  • 注意: ⌊ N p i ⌋ \lfloor{\frac{N}{p^i}}\rfloor piN指的是1~N中含有因子 p i p^i pi的数的个数,例如N=10、p=2时, ⌊ N p ⌋ \lfloor{\frac{N}{p}}\rfloor pN= ⌊ 10 2 ⌋ \lfloor{\frac{10}{2}}\rfloor 210=5,即 1~10之间含有因子2的数有5个:2、4、6、8、10。 ⌊ N p 2 ⌋ \lfloor{\frac{N}{p^2}}\rfloor p2N= ⌊ 10 4 ⌋ \lfloor{\frac{10}{4}}\rfloor 410=2,含有4为因子的数有2个:4、8。
  • 一张可能有助于理解的图:
    在这里插入图片描述

AC代码:

/ /AC代码:
#include<cstdio>
const int maxn=1e6+20;
int vis[maxn];
int primes[maxn];
int get_primes(int n){/ /得到1~N之间的素数表,并返回素数个数
int cnt=0;
for(int i=2;i<=n;i++){
if(vis[i]==0){primes[cnt]=i;cnt++;}    
 for(int j=0;j<cnt;j++){
 if(i*primes[j]>n) break;
 vis[i*primes[j]]=1;    
 if(i%primes[j]==0) break; 
 }       
 }
return  cnt;
}
int main(){
int N;
scanf("%d",&N);
int cnt=get_primes(N);
for(int i=0;i<cnt;i++){ 
int p=primes[i],n=N,c=0; / /用c统计当前质数P的个数
while(n){  / /直到n/p为0,即n除p向下取整为0
 c+=n/p;   
 n=n/p;    
}
if(c!=0) printf("%d %d\n",primes[i],c);    
} 
return 0;    
}

上一篇博客:素数筛详解(从零开始优化!)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值