Kimi LeetCode 3715. 完全平方数的祖先个数总和 Python3实现
以下是 LeetCode 3715. 完全平方数的祖先个数总和 的 Python3 实现,基于 DFS + 哈希表 + 无平方因子核预处理的思路。
---
核心思路
关键数学性质:两个数 `a × b` 是完全平方数,当且仅当它们去掉所有平方因子后的剩余部分(无平方因子核)相等。
例如:
- `8 = 2³`,去掉 `2²`(偶数指数),剩余 `2¹` → `core[8] = 2`
- `2 = 2¹` → `core[2] = 2`
- `16 = 2⁴` → 所有指数都是偶数 → `core[16] = 1`
所以 `nums[i] × nums[ancestor]` 为完全平方数 ⇔ `core[nums[i]] == core[nums[ancestor]]`
算法:
1. 预处理所有数的无平方因子核 `core[x]`
2. DFS 遍历树,用哈希表 `cnt` 记录当前路径上各 `core` 值的出现次数
3. 对每个非根节点,查询 `cnt[core[nums[i]]]` 即为符合条件的祖先个数
4. 回溯时恢复现场
---
Python3 代码
```python
from typing import List
from collections import defaultdict
class Solution:
# 预处理:计算每个数的无平方因子核(square-free core)
# core[x] = 去掉 x 中所有平方因子后剩余的部分
MX = 100001
core = [0] * MX
@classmethod
def _init_core(cls):
"""类方法,只需执行一次预处理"""
if cls.core[1] != 0:
return # 已经初始化过
for i in range(1, cls.MX):
if cls.core[i] == 0: # i 本身是无平方因子数
j = 1
while i * j * j < cls.MX:
cls.core[i * j * j] = i
j += 1
def sumOfAncestors(self, n: int, edges: List[List[int]], nums: List[int]) -> int:
self._init_core()
# 建图(无向树)
g = [[] for _ in range(n)]
for u, v in edges:
g[u].append(v)
g[v].append(u)
ans = 0
# cnt[c] = 当前DFS路径上 core 值为 c 的节点个数
cnt = defaultdict(int)
def dfs(x: int, fa: int):
nonlocal ans
c = self.core[nums[x]]
# 当前节点的祖先中,core 值相同的个数
ans += cnt[c]
cnt[c] += 1
for y in g[x]:
if y != fa:
dfs(y, x)
# 回溯:恢复现场
cnt[c] -= 1
dfs(0, -1)
return ans
```
---
复杂度分析
项目 复杂度
预处理 `core` O(MX \cdot \sqrt{MX}),实际约 10^5 \times 10
建图 O(n)
DFS 遍历 O(n)
总时间 O(n + MX \cdot \sqrt{MX})
空间 O(n + MX)
---
示例验证
示例 1:`n=3, edges=[[0,1],[1,2]], nums=[2,8,2]`
节点 nums core 祖先core计数 贡献
0 2 2 `{}` 0
1 8 2 `{2:1}` 1
2 2 2 `{2:2}` 2
总和 = `0 + 1 + 2 = 3` ✓
示例 2:`n=3, edges=[[0,1],[0,2]], nums=[1,2,4]`
节点 nums core 祖先core计数 贡献
0 1 1 `{}` 0
1 2 2 `{1:1}` 0
2 4 1 `{1:1}` 1
总和 = `0 + 0 + 1 = 1` ✓
---
另一种写法(更简洁,直接修改 nums)
如果不想用类变量预处理,也可以直接对每个 `nums[i]` 做质因数分解,去掉所有平方因子:
```python
from typing import List
from collections import defaultdict
class Solution:
def sumOfAncestors(self, n: int, edges: List[List[int]], nums: List[int]) -> int:
# 预处理:去掉每个 nums[i] 中的所有平方因子
for i in range(n):
x = nums[i]
d = 2
while d * d <= x:
while x % (d * d) == 0:
x //= d * d
d += 1
nums[i] = x
g = [[] for _ in range(n)]
for u, v in edges:
g[u].append(v)
g[v].append(u)
ans = 0
cnt = defaultdict(int)
def dfs(x: int, fa: int):
nonlocal ans
c = nums[x]
ans += cnt[c]
cnt[c] += 1
for y in g[x]:
if y != fa:
dfs(y, x)
cnt[c] -= 1
dfs(0, -1)
return ans
```
两种方法本质相同,第二种更简洁,但第一种预处理 `core` 数组在多次调用时效率更高。两种方法都能通过 n \le 10^5 的数据范围。