《算法竞赛·快冲300题》将于2024年出版,是《算法竞赛》的辅助练习册。
所有题目放在自建的OJ New Online Judge。
用C/C++、Java、Python三种语言给出代码,以中低档题为主,适合入门、进阶。
“ 约数个数” ,链接: http://oj.ecustacm.cn/problem.php?id=1726
题目描述
【题目描述】 N!是一个很大的数字,请求出N!的约数个数。
【输入格式】 输入一个正整数N,不超过5*10^7。
【输出格式】 输出一个整数表示答案,答案对1e9+7求余数。
【输入样例】
5
【输出样例】
16
题解
这是一个经典题,用到素数筛和质因数分解。根据算术基本定理,任何一个正整数可以唯一地分解为有限个素数的乘积。本题先求出n!能分解出的所有素数,然后再统计约数的个数。n!的素数因子,只需要求比n小的素数即可。
【重点】 欧拉筛,质因数分解。
C++代码
下面的代码,先用欧拉筛euler_sieve()求得比n小的素数,存在primes[]中,其数量有cnt个。
然后在23行的for循环中,逐个对每个素数统计
n
!
n!
n! 的约数。
统计也用了素数筛的思想。例如
10
!
=
10
×
9
×
8
×
7
×
6
×
5
×
4
×
3
×
2
×
1
10! = 10×9×8×7×6×5×4×3×2×1
10!=10×9×8×7×6×5×4×3×2×1 ,第一个素数是2,
10
!
10!
10! 的因子中有多少个2?这样计算:10/2 = 5,10/4 = 2,10/8 = 1,共5+2+1=8个。为什么?
先按2的倍数刷一遍,即2、4、6、8、10,共5个;再按4的倍数刷一遍,即4、8,共2个;再按8的倍数刷一遍,即8,共1个。这样2的因子都被刷完了,共有5+2+1 = 8个2。
注意本题的空间限制是256M。第7行的bool vis[N]使用了50M,第8行的int primes[N/10]使用了4×5M=20M。如果第8行写成int primes[N],总空间有可能超过256M。
#include <bits/stdc++.h>
using namespace std;
const int N=5e7 + 10;
typedef long long ll;
ll mod = 1e9+7;
int cnt; //n以内素数的个数
bool vis[N]; //记录是否被筛
int primes[N/10]; //存素数。除以10可以省空间。1000万以内的素数不到100万个。
void euler_sieve(int n){ //欧拉筛
for(int i=2;i<=n;i++) {
if(!vis[i]) primes[cnt++]=i;
for(int j=0; j<cnt; j++){
if(i*primes[j]>n) break;
vis[primes[j]*i] = true;
if(i%primes[j]==0) break;
}
}
}
int main(){
int n; cin>>n;
euler_sieve(n);
ll ans = 1;
for(int i=0;i<cnt;i++){
ll p = primes[i], res=0; //注意这里p是ll类型
while(p<=n){
res += n/p; //res: 素数p在n!出现了res次
p *= primes[i];
}
ans = ans*(res+1) % mod;
}
cout<<ans<<endl;
return 0;
}
Java代码
import java.util.*;
public class Main {
static final int N = 50000010;
static int cnt;
static boolean[] vis = new boolean[N];
static int[] primes = new int[N/10];
static long mod = 1000000007;
static void euler_sieve(int n) {
for (int i = 2; i <= n; i++) {
if (!vis[i]) primes[cnt++] = i;
for (int j = 0; j < cnt && (long)i * primes[j] <= n; j++) {
vis[i * primes[j]] = true;
if (i % primes[j] == 0) break;
}
}
}
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
euler_sieve(n);
long ans = 1;
for (int i = 0; i < cnt; i++) {
long p = primes[i], res = 0;
while (p <= n) {
res += n / p;
p *= primes[i];
}
ans = ans * (res + 1) % mod;
}
System.out.println(ans);
}
}
Python代码
下面的python代码提交后显示“内存超限”。而且计算时间太长。这样的题目不适合用Python实现。
N = 50000010
cnt = 0
vis = [False] * N #print(sys.getsizeof(vis))打印vis占用的内存是400M
primes = [0] * (N//10)
mod = 1000000007
def euler_sieve(n):
global cnt
for i in range(2, n + 1):
if not vis[i]:
primes[cnt] = i
cnt += 1
for j in range(cnt):
if i * primes[j] > n: break
vis[i * primes[j]] = True
if i % primes[j] == 0: break
n = int(input())
euler_sieve(n)
ans = 1
for i in range(cnt):
p = primes[i]
res = 0
while p <= n:
res += n // p
p *= primes[i]
ans = ans * (res + 1) % mod
print(ans)