题目描述:
给定整数 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 ⌊p⌊logNP⌋N⌋。即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;
}