《算法竞赛·快冲300题》每日一题:“质因子数量”

算法竞赛·快冲300题》将于2024年出版,是《算法竞赛》的辅助练习册。
所有题目放在自建的OJ New Online Judge。
用C/C++、Java、Python三种语言给出代码,以中低档题为主,适合入门、进阶。

文章目录

  • 题目描述
  • 题解
  • C++代码
  • Java代码
  • Python代码

质因子数量” ,链接: http://oj.ecustacm.cn/problem.php?id=1780

题目描述

【题目描述】 给出n个数字,你可以任意选择一些数字相乘,相乘之后得到新数字x,x的分数等于x不同质因子数量。
  请你计算所有选择数字方案中,x分数的总和。答案对1000000007取模。
【输入格式】 输入第一行为一个正整数n。
  第二行包含n个正整数ai。(1≤n≤200000,1≤ai≤1000000)。
【输出格式】 输出一个整数表示答案。
【输入样例】

3
6 1 2

【输出样例】

10

题解

  样例{6, 1, 2}有3个数字,3个数字有 2 3 = 8 2^3 = 8 23=8种组合,每种组合内数字相乘得{ ∅ \emptyset , 1, 2, 6, 1×2, 1×6, 2×6, 1×2×6} = { ∅ \emptyset , 1, 2, 2×3, 2, 2×3, 2×2×3, 2×2×3},它们的质因子数量是{0, 0, 1, 2, 1, 2, 2, 2},总和是10。 ∅ \emptyset 表示空集。
  简单的办法是单独计算每个数的质因子,计算它的分数,然后统计n个数的总分数,但是这种简单办法会超时。对一个数a进行质因数分解是试除 2 2 2 ~ n \sqrt{n} n 的内的所有素数;对n个数做质因数分解,总复杂度为 n a n\sqrt{a} na ,超时。
  本题不用单独计算每个数的分数,而是统一计算。从n个数中任选数字相乘,共有 2 n 2^n 2n种组合。一个质数在一个组合中出现一次,答案加1;统计所有的质数在所有组合中出现的总次数,就是答案。一个质数可能在多少个组合中出现?设i是其中k个数的因子,那么它在 2 n − 2 n − k 2^n-2^{n-k} 2n2nk个组合中出现。例如样例{6, 1, 2}中,质数2是{6, 2}这2个数的因子,在{2, 6, 1×2, 1×6, 2×6, 1×2×6}这 2 3 − 2 3 − 2 = 6 2^3-2^{3-2} = 6 23232=6种组合中出现;3是{6}的因子,在{6, 1×6, 2×6, 1×2×6}这 2 3 − 2 3 − 1 = 4 2^3-2^{3-1} = 4 23231=4种组合中出现。答案是6+4 = 10。
  本题的解题步骤是:用素数筛得到所有的质数;统计每个质数在n个数中出现的次数k,用 2 n − 2 n − k 2^n-2^{n-k} 2n2nk计算它在所有组合中的分数。在做素数筛的同时,统计质数出现的次数,总计算量相当于只做了一次素数筛,复杂度 O ( a l o g 2 l o g 2 a ) O(alog_2log_2a) O(alog2log2a) a ≤ 1000000 a≤1000000 a1000000
【重点】 掌握质因数分解。

C++代码

  计算 2 n 2^n 2n不能用pow()库函数,因为n太大,而pow()不能做超大的幂计算;更不能直接用位运算2< 2 n 2^n 2n。第30行第1个MOD不能少,因为fastpow()返回取模的结果,导致fastpow(2, n) - fastpow(2, n - k)可能是负数,需要加上MOD保证为正。

#include
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
const int MOD = 1e9 + 7;
int cnt[N];
bool not_prime[N];
ll fastpow(ll a, ll n){         //快速幂,计算a^n,并取模
    ll ans = 1;
    while(n){
        if(n & 1)  ans = ans * a % MOD;
        a = a * a % MOD;
        n >>= 1;
    }
    return ans;
}
int main(){
    int n;  cin >> n;
    for(int i = 1; i <= n; i++) {
        int a;  scanf("%d",&a);
        cnt[a]++;
    }
    ll ans = 0;
    for(int i = 2; i < N; i++)                     //素数筛
        if(!not_prime[i]){                          //i是质数
            ll k = cnt[i];                          //统计质数i出现的次数k
            for(int j = 2*i; j < N; j += i)        //把i的倍数筛掉,留下的是素数
                k += cnt[j], not_prime[j] = true;   //统计质数i出现的次数k
            if(k)
                ans = (ans + fastpow(2, n) - fastpow(2, n-k) + MOD) % MOD;  //质数i的得分是2^n-2^(n-k)
        }
    cout<<ans<<endl;
    return 0;
}

Java代码

import java.util.Scanner;
public class Main {
    static final int N = 1000010;
    static final int MOD = 1000000007;
    static int[] cnt = new int[N];
    static boolean[] notPrime = new boolean[N];
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        for (int i = 0; i < n; i++) {
            int a = sc.nextInt();
            cnt[a]++;
        }
        long ans = 0;
        for (int i = 2; i < N; i++) {                  //素数筛
            if (!notPrime[i]) {                        //i是质数
                long k = cnt[i];                       //统计质数i出现的次数k
                for (int j = 2 * i; j < N; j += i) {   //把i的倍数筛掉,留下的是素数
                    k += cnt[j];                       //统计质数i出现的次数k
                    notPrime[j] = true;
                }
                if (k != 0) 
                    ans = (ans+fastPow(2,n) - fastPow(2,n-k) + MOD) % MOD; //质数i的得分是2^n-2^(n-k)
            }
        }
        System.out.println(ans);
        sc.close();
    }
    static long fastPow(long a, long n) {              //快速幂,计算a^n,并取模
        long ans = 1;
        while (n > 0) {
            if ((n & 1) == 1)  ans = ans * a % MOD;

            a = a * a % MOD;
            n >>= 1;
        }
        return ans;
    }
}

Python代码

N = 1000010
MOD = 1000000007
cnt = [0] * N
not_prime = [False] * N
def fastpow(a, n):                 #快速幂,计算a^n,并取模
    ans = 1
    while n:
        if n & 1:  ans = ans * a % MOD
        a = a * a % MOD
        n >>= 1
    return ans
n = int(input())
a = list(map(int, input().split()))
for i in a: cnt[i] += 1
ans = 0
for i in range(2, N):              #素数筛
    if not not_prime[i]:           #i是质数
        k = cnt[i]                 #统计质数i出现的次数k
        for j in range(2 * i, N, i):       #把i的倍数筛掉,留下的是素数
            k += cnt[j]                    #统计质数i出现的次数k
            not_prime[j] = True
        if k:
            ans = (ans + fastpow(2, n) - fastpow(2, n - k) + MOD) % MOD   #质数i的得分是2^n-2^(n-k)
print(ans)

你可能感兴趣的:(算法竞赛快冲300题,算法)