#P2787. 第2题-亲密数
-
1000ms
Tried: 45
Accepted: 13
Difficulty: 6
所属公司 :
阿里
时间 :2025年4月2日-阿里淘天(算法岗)
-
算法标签>暴力枚举
第2题-亲密数
题解
题面描述
给定一个长度为n的数组,数组中每个元素为ai。定义一组数为亲密数当且仅当这组数的乘积是一个完全平方数。要求从数组中选出至少一个元素,使得这些选出的元素构成一组亲密数,求所有可能的选取方案数。
思路
- 每个正整数的质因数分解可以表示成: ai = p1e1 ×p2e2 ×⋯×pkek
- 乘积是完全平方数当且仅当每个质因数的指数之和为偶数,即对于每个质因数都有:e1+e2+⋯+ek≡0(mod2)
- 我们可以将每个数转换为一个模2下的指数向量,即对于每个质因数,如果指数为奇数则记为1,偶数则记为0。由于乘法在指数上相加,在模2下加法对应异或操作,因此我们只需要对选中的数对应的向量异或,判断结果是否全为零。
- 由于n≤20,可以枚举所有非空子集(最多2n−1种情况),对于每个子集计算所有元素的模2指数和,若为全零则计数。
代码分析
- 预处理:利用筛法或直接枚举质数,将所有小于等于100的质数保存在数组中。
- 因数分解:对于每个数ai,依次判断每个质数能否整除,如果可以,则更新对应的模2指数(使用0/1记录)。
- 枚举子集:用位运算枚举所有非空子集,利用预处理好的每个数的“质数模2指数向量”(可以使用一个整数位掩码表示)计算异或结果,若结果为零则计数。
C++
#include <iostream>
#include <vector>
using namespace std;
// 求质数列表(所有小于等于100的质数)
vector<int> getPrimes() {
vector<int> primes;
bool isPrime[101] = {false};
for (int i = 2; i <= 100; i++) isPrime[i] = true;
for (int i = 2; i <= 100; i++) {
if (isPrime[i]) {
primes.push_back(i);
for (int j = i * i; j <= 100; j += i)
isPrime[j] = false;
}
}
return primes;
}
int main(){
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++){
cin >> a[i];
}
// 获取所有小于等于100的质数
vector<int> primes = getPrimes();
int pCount = primes.size();
// 对每个数计算质因数分解的模2表示,用位掩码表示
vector<int> mask(n, 0);
for (int i = 0; i < n; i++){
int x = a[i];
int curMask = 0;
for (int j = 0; j < pCount; j++){
int cnt = 0;
while(x % primes[j] == 0){
cnt++;
x /= primes[j];
}
// 如果指数为奇数,则设置第j位为1
if(cnt % 2 == 1) {
curMask |= (1 << j);
}
}
mask[i] = curMask;
}
long long ans = 0;
// 枚举所有非空子集,共计 2^n - 1 种情况
int total = 1 << n;
for (int s = 1; s < total; s++){
int xorSum = 0;
// 对子集中的每个元素进行异或累加
for (int i = 0; i < n; i++){
if(s & (1 << i)){
xorSum ^= mask[i];
}
}
// 如果异或结果为0,则乘积为完全平方数
if(xorSum == 0)
ans++;
}
cout << ans << endl;
return 0;
}
Python
# -*- coding: utf-8 -*-
import math
# 获取所有小于等于100的质数
def get_primes():
primes = []
is_prime = [True] * 101
for i in range(2, 101):
if is_prime[i]:
primes.append(i)
for j in range(i * i, 101, i):
is_prime[j] = False
return primes
def main():
n = int(input().strip())
a = list(map(int, input().split()))
primes = get_primes() # 所有质数
pCount = len(primes)
# 对每个数计算质因数分解模2的表示,使用位掩码存储
masks = [0] * n
for i in range(n):
x = a[i]
cur_mask = 0
for j in range(pCount):
cnt = 0
while x % primes[j] == 0:
cnt += 1
x //= primes[j]
# 如果指数为奇数,则设置对应位为1
if cnt % 2 == 1:
cur_mask |= (1 << j)
masks[i] = cur_mask
ans = 0
total = 1 << n # 总共2^n种子集
# 枚举所有非空子集
for s in range(1, total):
xor_sum = 0
for i in range(n):
if s & (1 << i):
xor_sum ^= masks[i]
# 如果异或结果为0,说明乘积为完全平方数
if xor_sum == 0:
ans += 1
print(ans)
if __name__ == "__main__":
main()
Java
import java.util.*;
import java.io.*;
public class Main {
// 获取所有小于等于100的质数
public static ArrayList<Integer> getPrimes() {
ArrayList<Integer> primes = new ArrayList<>();
boolean[] isPrime = new boolean[101];
Arrays.fill(isPrime, true);
for (int i = 2; i <= 100; i++) {
if(isPrime[i]) {
primes.add(i);
for (int j = i * i; j <= 100; j += i) {
isPrime[j] = false;
}
}
}
return primes;
}
public static void main(String[] args) throws Exception {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(); // 数组长度
int[] a = new int[n];
for (int i = 0; i < n; i++){
a[i] = sc.nextInt();
}
ArrayList<Integer> primes = getPrimes(); // 所有小于等于100的质数
int pCount = primes.size();
int[] masks = new int[n];
// 对每个数计算质因数分解模2的表示,存储在整数掩码中
for (int i = 0; i < n; i++){
int x = a[i];
int curMask = 0;
for (int j = 0; j < pCount; j++){
int cnt = 0;
int p = primes.get(j);
while (x % p == 0) {
cnt++;
x /= p;
}
// 如果指数为奇数,则将第j位设为1
if (cnt % 2 == 1) {
curMask |= (1 << j);
}
}
masks[i] = curMask;
}
long ans = 0;
int total = 1 << n; // 总共2^n个子集
// 枚举所有非空子集
for (int s = 1; s < total; s++){
int xorSum = 0;
for (int i = 0; i < n; i++){
if ((s & (1 << i)) != 0) {
xorSum ^= masks[i];
}
}
// 若异或结果为0,则该子集乘积为完全平方数
if (xorSum == 0) {
ans++;
}
}
System.out.println(ans);
}
}
题目内容
小红定义一组是亲密数,当且仅当这组数的乘积是完全平方数。
现在小红拿到了一个数组,她希望选出若干个元素(不能一个都不选)使得它们是一组亲密数。小红想知道有多少种选择方案?
输入描述
第一行输入一个正整数 n ,表示数组的长度。
第二行输入 n 个 正整数 a1,a2,...,an,表示数组。
1≤n≤20
1≤ai≤100
输出描述
输出一个整数,表示有多少种选择方案。
样例1
输入
6
1 2 3 4 5 6
输出
7
说明
一共存在 7 种方案:
选择元素 [1]
选择元素 [4]
选择元素 [1,4]
选择元素 [2,3,6]
选择元素 [1,2,3,6]
选择元素 [4,2,3,6]
选择元素 [1,4,2,3,6]