《算法竞赛·快冲300题》每日一题:“约数个数”

算法竞赛·快冲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)

猜你喜欢

转载自blog.csdn.net/weixin_43914593/article/details/131819625
今日推荐