image-20250424201815331

题目大意

题目的核心是要处理一棵树上每个节点的权值,对于拥有两个及以上儿子节点的父节点,要保证所有儿子节点的权值两两相乘不能是完全平方数,目标是求出最少需要修改多少个节点的权值,才能让整棵树满足这个条件。

思路

完全平方数的判定

首先什么是完美平方数,如果一个正整数 a 是某一个整数 b 的平方,那么这个正整数 a 叫做完全平方数。零也可称为完全平方数。

两个数 xy 的乘积是完全平方数,当且仅当 xy 的乘积中,所有质因子的幂次都是偶数。

例如 4 * 9 = 36 是完全平方数,因为 4 = 22,9 = 32 ,他们的质因子的幂次都是偶数。

进一步推导,这个条件等价于 xy 的 “平方因子化简后” 的形式相同。所谓 “平方因子化简”,就是对每个数 a 分解质因数后,只保留每个质因数的奇数次幂(即 ai 的“平方自由部分”),这部分记作 f(ai)

f(ai) = f(aj) ,那么 ai * aj 必然是完全平方数。

代码处理

使用邻接表存树,如果存在两个儿子节点的 f(aj) 相等,那么这两个儿子节点权值的乘积就是完全平方数,不满足题目要求。

贪心处理:

对每个有 k ≥ 2 个儿子的结点 i,统计其所有儿子的 f(aj),对于重复的 f(aj),需要修改其中 cnt − 1 个结点的权值(cnt 为该 f(aj) 出现次数)。 对每个结点,累加需要修改的次数。

关于squareFree(int x) — 求平方自由部分:
我们需要只保留不能被2整除的幂次部分,所以按照如下形式解耦出平方自由部分

1
2
3
4
5
6
7
8
9
10
11
12
13
private int squareFree(int x) {
int res = 1;
for (int i = 2; i * i <= x; i++) { // 枚举所有可能的质因数
int cnt = 0;
while (x % i == 0) { // 统计i作为质因子的次数
x /= i;
cnt++;
}
if ((cnt & 1) == 1) res *= i; // 只保留奇数次的质因数
}
if (x > 1) res *= x; // x本身是大于1的质数
return res;
}

完整代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//package 数学.subject.P12255_蓝桥杯2024国JavaB_园丁;

import java.util.*;
import java.io.*;

public class Main {
public static void main(String[] args) throws IOException {
new Solutions2();
}
}


class Solutions2{
private int n;
private int[] a;
private int[] f;
private List<Integer>[] tree;
private int ans = 0;

private int squareFree(int x) {
int res = 1;
for (int i = 2; i * i <= x; i++) {
if (x % i == 0) {
int cnt = 0;
while (x % i == 0) {
x /= i;
cnt++;
}
if (cnt % 2 != 0) {
res *= i;
}
}
}
if (x > 1) {
res *= x;
}
return res;
}

public Solutions2(){
FastReader sc = new FastReader();
n = sc.nextInt();
a = new int[n + 1];
f = new int[n + 1];
for (int i = 1; i <= n; i++) {
a[i] = sc.nextInt();
f[i] = squareFree(a[i]);
}

tree = new ArrayList[n + 1];
for (int i = 1; i <= n; i++) tree[i] = new ArrayList<>();
for (int i = 1; i < n; i++) {
int u = sc.nextInt();
int v = sc.nextInt();
tree[u].add(v);
tree[v].add(u);
}

// 使用迭代的后序遍历来替代递归DFS
Deque<Object[]> stack = new ArrayDeque<>();
stack.push(new Object[]{1, 0, false});

while (!stack.isEmpty()) {
Object[] node = stack.pop();
int u = (Integer) node[0];
int fa = (Integer) node[1];
boolean visited = (Boolean) node[2];

if (!visited) {
stack.push(new Object[]{u, fa, true});
List<Integer> children = new ArrayList<>();
for (int v : tree[u]) {
if (v != fa) {
children.add(v);
}
}
// 逆序压入,以保持原来的处理顺序
for (int i = children.size() - 1; i >= 0; i--) {
int v = children.get(i);
stack.push(new Object[]{v, u, false});
}
} else {
List<Integer> children = new ArrayList<>();
for (int v : tree[u]) {
if (v != fa) {
children.add(v);
}
}
if (children.size() >= 2) {
Map<Integer, Integer> cnt = new HashMap<>();
for (int v : children) {
int sf = f[v];
cnt.put(sf, cnt.getOrDefault(sf, 0) + 1);
}
for (int c : cnt.values()) {
if (c > 1) {
ans += c - 1;
}
}
}
}
}
System.out.println(ans);
}

class FastReader {
BufferedReader br;
StringTokenizer st;
public FastReader() {
br = new BufferedReader(new InputStreamReader(System.in));
st = new StringTokenizer("");
}
String next() {
while (!st.hasMoreElements()) {
try {
st = new StringTokenizer(br.readLine());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return st.nextToken();
}
int nextInt() {
return Integer.parseInt(next());
}
}
}
image-20250425001434729