小红的完全平方数

[题目链接](https://www.nowcoder.com/practice/8e83151b7a5b4e1196eb9aa4c4ff7d95)

思路

数组是"好数组"当且仅当任意两个元素的乘积都是完全平方数。每次操作可以选一个元素乘以任意正整数,求最少操作次数。

核心观察:无平方因子部分

对于正整数 ,将其质因数分解为 ,定义 无平方因子部分(square-free part)为:

$$

即保留所有指数为奇数的质因子,各取一次。例如

关键性质:两个正整数 的乘积 是完全平方数,当且仅当

证明 是完全平方数等价于 的所有质因子指数均为偶数。对每个质因子 ,设 中指数为 中指数为 ,则需要 为偶数,即 奇偶性相同。这恰好就是

贪心策略

数组是好数组当且仅当所有元素的无平方因子部分相同。将每个元素乘以某个正整数可以任意改变其无平方因子部分,因此每次操作恰好能"修正"一个元素。

为使操作次数最少,应保留出现次数最多的那组无平方因子部分不动,将其余元素各操作一次:

$$

计算无平方因子部分

对每个 ,试除 的所有质因子,统计每个质因子出现次数的奇偶性。若某个质因子出现奇数次,则乘入 。试除结束后若剩余值大于 ,说明有一个大质因子,也乘入

复杂度

  • 时间复杂度:,其中 为元素的最大值。每个元素的试除法需
  • 空间复杂度:,用于哈希表存储无平方因子部分的计数。

代码

#include <bits/stdc++.h>
using namespace std;

int main(){
    int n;
    scanf("%d", &n);
    map<long long, int> cnt;
    int zeros = 0;
    for(int i = 0; i < n; i++){
        long long x;
        scanf("%lld", &x);
        if(x == 0){
            zeros++;
            continue;
        }
        if(x < 0) x = -x;
        long long sf = 1;
        for(long long p = 2; p * p <= x; p++){
            int c = 0;
            while(x % p == 0){
                x /= p;
                c++;
            }
            if(c % 2 == 1) sf *= p;
        }
        if(x > 1) sf *= x;
        cnt[sf]++;
    }
    int mx = 0;
    for(auto& [k, v] : cnt){
        mx = max(mx, v);
    }
    int nonzeros = n - zeros;
    int ans = nonzeros - mx;
    printf("%d\n", ans);
    return 0;
}
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        Map<Long, Integer> cnt = new HashMap<>();
        int zeros = 0;
        for (int i = 0; i < n; i++) {
            long x = sc.nextLong();
            if (x == 0) {
                zeros++;
                continue;
            }
            if (x < 0) x = -x;
            long sf = 1;
            for (long p = 2; p * p <= x; p++) {
                int c = 0;
                while (x % p == 0) {
                    x /= p;
                    c++;
                }
                if (c % 2 == 1) sf *= p;
            }
            if (x > 1) sf *= x;
            cnt.merge(sf, 1, Integer::sum);
        }
        int mx = 0;
        for (int v : cnt.values()) {
            mx = Math.max(mx, v);
        }
        int nonzeros = n - zeros;
        int ans = nonzeros - mx;
        System.out.println(ans);
    }
}