题目链接

小红的完全平方数

题目描述

给定一个长度为 的整数数组。如果数组中任意两个数 的乘积都是完全平方数,则称该数组为“好数组”。

小红可以执行操作:选择数组中的一个数,将其乘以任意正整数。她想知道,最少需要多少次操作才能使数组变成一个“好数组”。

解题思路

这是一个最优化问题,关键在于将“好数组”的性质转化为一个更易于处理的数学条件。

“好数组”的性质

一个数是完全平方数,当且仅当其所有质因子的指数都是偶数。

考虑两个数 。设它们的质因数分解分别为:

它们的乘积为:

要使 是完全平方数,对于每一个质因子 ,其指数 都必须是偶数。这意味着,对于任意一个质因子 ,指数 奇偶性必须相同

这个结论必须对数组中任意两个数都成立。因此,一个数组是“好数组”的充要条件是:数组中所有数字,其每一个质因子的指数,都具有相同的奇偶性。

“无平方因子核心”

我们可以对每个数提取其“本质”部分。任何一个正整数 都可以唯一地表示为 ,其中 是一个无平方因子数(square-free number),即 的任何质因子指数都为 1。我们称 的**“无平方因子核心”**。

例如:

  • ,核心是
  • ,核心是
  • ,核心是
  • ,核心是

这个核心 是由 中所有指数为奇数的质因子相乘得到的。

现在,我们用核心来重新审视“好数组”的条件。

为了使 是完全平方数, 必须是完全平方数。由于 本身都是无平方因子数,它们的乘积是完全平方数的唯一可能是

因此,“好数组”的条件等价于:数组中所有数字的“无平方因子核心”都相同。

贪心策略

小红的操作是“乘以一个正整数”。这个操作非常强大,可以把任意一个数 的核心变为任意我们想要的目标核心 。例如,要将 core(a_i) 变为 ,只需将 乘以 即可。

我们的目标是让所有数的核心都相同,且操作次数最少。这等价于,我们选择一个目标核心 ,然后保留所有核心已经是 的数,只修改那些核心不是 的数。为了让修改的数最少,我们应该选择数组中出现次数最多的核心作为我们的目标核心

算法步骤

  1. 计算核心:遍历数组中的每一个数 ,计算出它的“无平方因子核心”。
    • 可以通过试除法实现:从 2 开始,到 ,分解质因子。对于每个质因子,如果其在 中的指数是奇数,就将其乘入核心值。
  2. 统计频率:使用哈希表(map)来统计每个核心值出现的次数。
  3. 找出最大频率:遍历哈希表,找到出现次数最多的核心,其频率为 max_freq
  4. 计算答案:我们选择保留这 max_freq 个数,它们无需操作。剩下的 个数都需要进行一次操作。因此,最少操作次数就是

代码

#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
#include <cmath>

using namespace std;

// 计算一个数的无平方因子核心
int get_square_free_core(int n) {
    int core = 1;
    for (int i = 2; i * i <= n; ++i) {
        if (n % i == 0) {
            int count = 0;
            while (n % i == 0) {
                count++;
                n /= i;
            }
            if (count % 2 == 1) {
                core *= i;
            }
        }
    }
    if (n > 1) { // 如果n还有剩余,说明它是一个质数
        core *= n;
    }
    return core;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int n;
    cin >> n;

    map<int, int> core_counts;
    for (int i = 0; i < n; ++i) {
        int a;
        cin >> a;
        core_counts[get_square_free_core(a)]++;
    }

    int max_freq = 0;
    if (n > 0) {
        for (auto const& [core, count] : core_counts) {
            if (count > max_freq) {
                max_freq = count;
            }
        }
    }

    cout << n - max_freq << endl;

    return 0;
}
import java.util.Scanner;
import java.util.HashMap;
import java.util.Map;

public class Main {
    // 计算一个数的无平方因子核心
    private static int getSquareFreeCore(int n) {
        int core = 1;
        for (int i = 2; i * i <= n; i++) {
            if (n % i == 0) {
                int count = 0;
                while (n % i == 0) {
                    count++;
                    n /= i;
                }
                if (count % 2 == 1) {
                    core *= i;
                }
            }
        }
        if (n > 1) { // 如果n还有剩余,说明它是一个质数
            core *= n;
        }
        return core;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();

        Map<Integer, Integer> coreCounts = new HashMap<>();
        for (int i = 0; i < n; i++) {
            int a = sc.nextInt();
            int core = getSquareFreeCore(a);
            coreCounts.put(core, coreCounts.getOrDefault(core, 0) + 1);
        }

        int maxFreq = 0;
        if (n > 0) {
            for (int count : coreCounts.values()) {
                if (count > maxFreq) {
                    maxFreq = count;
                }
            }
        }

        System.out.println(n - maxFreq);
    }
}
import sys
from collections import Counter

def get_square_free_core(n):
    """计算一个数的无平方因子核心"""
    core = 1
    i = 2
    while i * i <= n:
        if n % i == 0:
            count = 0
            while n % i == 0:
                count += 1
                n //= i
            if count % 2 == 1:
                core *= i
        i += 1
    if n > 1:
        core *= n
    return core

def solve():
    n_str = sys.stdin.readline()
    if not n_str: return
    n = int(n_str)
    
    a = list(map(int, sys.stdin.readline().split()))

    if n == 0:
        print(0)
        return

    cores = [get_square_free_core(x) for x in a]
    
    if not cores:
        print(0)
        return
        
    core_counts = Counter(cores)
    max_freq = core_counts.most_common(1)[0][1]
    
    print(n - max_freq)

solve()

算法及复杂度

  • 算法:数论 (质因数分解) + 贪心 + 哈希表
  • 时间复杂度。其中 是数组元素的数量, 是数组中的最大值。主要开销在于对 个数中的每一个都进行一次试除法来计算其“无平方因子核心”,每次计算的时间复杂度为
  • 空间复杂度。在最坏情况下,所有 个数的核心都不同,哈希表需要存储 个条目。