
以下是 LeetCode 3525 求出数组的 X 值 II 的 Python3 实现核心思路是线段树维护区间前缀积的模分布。思路概述- 对于每个查询先单点修改 nums[index]再移除前缀保留 [start..]最后可以删除任意后缀即保留任意非空前缀。- 因此每个查询的答案等价于在子数组 nums[start..] 的所有非空前缀中乘积模 k 等于 x 的个数。- 由于 k ≤ 5线段树每个节点只需维护- prod区间整体乘积模 k- cntcnt[m] 表示该区间内有多少个非空前缀的乘积模 k 等于 m- 合并两个子区间时右子树的前缀需要乘上左子树的总积再模 k。复杂度- 时间O((n q) · k² · log n)由于 k ≤ 5实际约为 O((n q) log n)- 空间O(n)完整代码pythonclass Node:__slots__ (prod, cnt)def __init__(self):self.prod 1 # 区间乘积 % kself.cnt [0] * 5 # cnt[m] 非空前缀中乘积 % k m 的个数class SegTree:def __init__(self, nums, k):self.n len(nums)self.k kself.tree [Node() for _ in range(self.n * 4)]self.build(1, 0, self.n - 1, nums)def build(self, node, l, r, nums):if l r:v nums[l] % self.kself.tree[node].prod vself.tree[node].cnt [0] * 5self.tree[node].cnt[v] 1returnmid (l r) 1self.build(node 1, l, mid, nums)self.build(node 1 | 1, mid 1, r, nums)self.pull(node)def merge(self, left, right):res Node()res.prod (left.prod * right.prod) % self.k# 左区间的前缀直接保留for i in range(self.k):res.cnt[i] left.cnt[i]# 右区间的前缀需要乘上左区间的总积for i in range(self.k):if right.cnt[i]:new_mod (left.prod * i) % self.kres.cnt[new_mod] right.cnt[i]return resdef pull(self, node):self.tree[node] self.merge(self.tree[node 1], self.tree[node 1 | 1])def update(self, node, l, r, idx, val):if l r:v val % self.kself.tree[node].prod vself.tree[node].cnt [0] * 5self.tree[node].cnt[v] 1returnmid (l r) 1if idx mid:self.update(node 1, l, mid, idx, val)else:self.update(node 1 | 1, mid 1, r, idx, val)self.pull(node)def query(self, node, l, r, ql, qr):if ql l and r qr:return self.tree[node]mid (l r) 1if qr mid:return self.query(node 1, l, mid, ql, qr)if ql mid:return self.query(node 1 | 1, mid 1, r, ql, qr)left self.query(node 1, l, mid, ql, qr)right self.query(node 1 | 1, mid 1, r, ql, qr)return self.merge(left, right)class Solution:def resultArray(self, nums: List[int], k: int, queries: List[List[int]]) - List[int]:n len(nums)# 预处理所有数先对 k 取模nums [x % k for x in nums]seg SegTree(nums, k)ans []for q in queries:idx, val, start, x qval % kseg.update(1, 0, n - 1, idx, val)res seg.query(1, 0, n - 1, start, n - 1)ans.append(res.cnt[x])return ans关键点说明1. cnt 的含义每个节点维护的是从该区间左端点开始的所有非空前缀的模分布。这样查询任意子区间时递归返回的子节点天然满足从左端点开始的要求合并时只需把右子树的前缀乘上左子树的总积即可。2. k ≤ 5 的利用cnt 数组固定开 5实际只用前 k 个合并时的双重循环最多 25 次运算常数极小。3. 单点更新修改叶子后自底向上 pull保持每个节点的 prod 和 cnt 正确。4. 预处理取模建树前和更新时都把数值对 k 取模避免大数运算。