题解:P12255 [蓝桥杯 2024 国 Java B] 园丁
题目大意
题目的核心是要处理一棵树上每个节点的权值,对于拥有两个及以上儿子节点的父节点,要保证所有儿子节点的权值两两相乘不能是完全平方数,目标是求出最少需要修改多少个节点的权值,才能让整棵树满足这个条件。
思路
完全平方数的判定
首先什么是完美平方数,如果一个正整数
两个数
例如
进一步推导,这个条件等价于
若
代码处理
使用邻接表存树,如果存在两个儿子节点的
贪心处理:
对每个有
关于求平方自由部分squareFree(int x):
我们需要只保留不能被2整除的幂次部分,所以按照如下形式解耦出平方自由部分。
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;
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class Main {
public static void main(String[] args) {
new Solutions();
}
}
class Solutions {
private int n ,ans = 0; // 树中节点的数量
private int[] a = new int[100086]; // 节点值
private int[] f = new int[100086]; // 节点值的平方自由部分
private List<Integer>[] tree; // 邻接表表示树
// 返回 x 的平方自由部分
// 平方自由部分是指一个数分解质因数后,每个质因数的指数都为 1 的部分
private int squareFree(int x) {
int res = 1;
for (int i = 2; i * i <= x; i++) {
int cnt = 0;
while (x % i == 0) {
x /= i;
cnt++;
}
if ((cnt & 1) == 1) res *= i; // 如果质因数的幂次为奇数,将其乘到结果中
}
if (x > 1) res *= x; // 如果 x 还有剩余的质因数,也乘到结果中
return res;
}
private void dfs(int u, int fa) {
List<Integer> children = new ArrayList<>();
// 遍历当前节点 u 的所有邻接节点 v
for(int v: tree[u]) {
// 不是父节点,加入children
if(v != fa){
children.add(v);
dfs(v, u); // 递归调用
}
}
if (children.size() >= 2) {
// 统计每个子节点的平方自由部分的出现次数
Map<Integer, Integer> cnt = new HashMap<>();
for(int v: children){
cnt.put(f[v], cnt.getOrDefault(f[v], 0) + 1);
}
for (int c : cnt.values()) {
if (c > 1) ans += c - 1;
}
}
}
public Solutions(){
FastReader sc = new FastReader();
n = sc.nextInt();
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);
}
// 从根节点(节点 1)开始进行深度优先搜索
dfs(1, 0);
// 输出最终结果
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());
}
}
}
但是,上述代码会出现递归调用过深的问题,我们需要将原来的递归深度优先搜索(DFS)改为迭代方式。通过使用栈来模拟后序遍历,我们可以避免递归调用过深的问题。
//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());
}
}
}