当前位置: 首页 > news >正文

Kimi LeetCode 3525. 求出数组的 X 值 II Go实现

以下是 LeetCode 3525 求出数组的 X 值 II 的 Go 实现,核心思路是线段树维护区间前缀积的模分布。

思路概述

- 对于每个查询,先单点修改 `nums[index]`,再移除前缀保留 `[start..]`,最后可以删除任意后缀(即保留任意非空前缀)。
- 因此每个查询的答案等价于:在子数组 `nums[start..]` 的所有非空前缀中,乘积模 `k` 等于 `x` 的个数。
- 由于 `k ≤ 5`,线段树每个节点只需维护:
- `prod`:区间整体乘积模 `k`
- `cnt`:`cnt[m]` 表示该区间内,有多少个非空前缀的乘积模 `k` 等于 `m`
- 合并两个子区间时,右子树的前缀需要乘上左子树的总积再模 `k`。

复杂度

- 时间:`O((n + q) · k² · log n)`,由于 `k ≤ 5`,实际约为 `O((n + q) log n)`
- 空间:`O(n)`

完整代码

```go
type Node struct {
prod int // 区间乘积 % k
cnt [5]int // cnt[m] = 非空前缀中乘积 % k == m 的个数
}

type SegTree struct {
k int
tree []Node
n int
}

func NewSegTree(nums []int, k int) *SegTree {
n := len(nums)
seg := &SegTree{
k: k,
tree: make([]Node, n*4),
n: n,
}
seg.build(1, 0, n-1, nums)
return seg
}

func (s *SegTree) build(node, l, r int, nums []int) {
if l == r {
v := nums[l] % s.k
s.tree[node].prod = v
s.tree[node].cnt = [5]int{}
s.tree[node].cnt[v] = 1
return
}
mid := (l + r) >> 1
s.build(node<<1, l, mid, nums)
s.build(node<<1|1, mid+1, r, nums)
s.pull(node)
}

func (s *SegTree) merge(left, right Node) Node {
var res Node
res.prod = (left.prod * right.prod) % s.k

// 左区间的前缀直接保留
for i := 0; i < s.k; i++ {
res.cnt[i] += left.cnt[i]
}

// 右区间的前缀需要乘上左区间的总积
for i := 0; i < s.k; i++ {
if right.cnt[i] != 0 {
newMod := (left.prod * i) % s.k
res.cnt[newMod] += right.cnt[i]
}
}

return res
}

func (s *SegTree) pull(node int) {
s.tree[node] = s.merge(s.tree[node<<1], s.tree[node<<1|1])
}

func (s *SegTree) update(node, l, r, idx, val int) {
if l == r {
v := val % s.k
s.tree[node].prod = v
s.tree[node].cnt = [5]int{}
s.tree[node].cnt[v] = 1
return
}
mid := (l + r) >> 1
if idx <= mid {
s.update(node<<1, l, mid, idx, val)
} else {
s.update(node<<1|1, mid+1, r, idx, val)
}
s.pull(node)
}

func (s *SegTree) query(node, l, r, ql, qr int) Node {
if ql <= l && r <= qr {
return s.tree[node]
}
mid := (l + r) >> 1
if qr <= mid {
return s.query(node<<1, l, mid, ql, qr)
}
if ql > mid {
return s.query(node<<1|1, mid+1, r, ql, qr)
}
left := s.query(node<<1, l, mid, ql, qr)
right := s.query(node<<1|1, mid+1, r, ql, qr)
return s.merge(left, right)
}

func resultArray(nums []int, k int, queries [][]int) []int {
n := len(nums)
for i := range nums {
nums[i] %= k
}

seg := NewSegTree(nums, k)
ans := make([]int, 0, len(queries))

for _, q := range queries {
idx, val, start, x := q[0], q[1]%k, q[2], q[3]
seg.update(1, 0, n-1, idx, val)
res := seg.query(1, 0, n-1, start, n-1)
ans = append(ans, res.cnt[x])
}

return ans
}
```

关键点说明

1. `cnt` 的含义:每个节点维护的是从该区间左端点开始的所有非空前缀的模分布。这样查询任意子区间时,递归返回的子节点天然满足"从左端点开始"的要求,合并时只需把右子树的前缀乘上左子树的总积即可。
2. `k ≤ 5` 的利用:`cnt` 数组固定开 5,实际只用前 `k` 个;合并时的双重循环最多 25 次运算,常数极小。
3. 单点更新:修改叶子后自底向上 `pull`,保持每个节点的 `prod` 和 `cnt` 正确。
4. 预处理取模:建树前和更新时都把数值对 `k` 取模,避免大数运算。

http://www.jsqmd.com/news/1158636/

相关文章:

  • 2026研发费用加计扣除稽查升级:金税四期“以数治税”+“实质认定”精准锁定违规
  • 2026年6月最新福州伯爵官方售后服务热线与网点地址查询 - 亨得利官方服务中心
  • 用Spec-Kit+Claude Code高效规划C++五子棋项目:告别文档焦虑
  • BepInEx Unity插件开发:从原理到实战的完整指南
  • AI编程助手选型指南:从环境适配到实战对比
  • OpenClaw 2.6.4 Windows一键安装包实战指南
  • 2026最新:自动视频总结选什么工具?这3款免费实用神器亲测好用
  • 在线考试系统盘点:2026年在线考试系统稳定可靠的品牌推荐
  • CBCX外汇的服务节奏清楚吗?
  • STM32F103C8T6 CMSIS-DAP 固件编译:从 GitHub 源码到 BluePill 烧录 5 步指南
  • 自媒体矩阵批量分发实战
  • MCP Server Boot Starters:快速构建稳定AI工具服务器的实战指南
  • 四川纯水系统生产厂家选型参考与常见问题梳理
  • Qwen3-TTS:面向边缘部署的可裁剪流式语音合成框架
  • 传统 HCM 仅线上化流程,数据割裂无自动洞察,人才风险只能事后复盘
  • AI服务区域限制与Claude API连接错误排查指南
  • Spire.Doc for Python 3.12 实战:Word 转 HTML 保留 3 种关键格式(图片/表单/页眉)
  • GitHub 4.4万星提示词库:从入门到精通的Prompt工程实战指南
  • AI安全检测系统优化:从实验室模型到工业级实战的架构与算法演进
  • 内容矩阵批量分发实战
  • 纯HTML5播放器带倍速、带画质切换的播放器
  • 2026年AI文献综述写作神器:从3周缩短到3天,这方法太香了!
  • PacBio + Hi-C + Illumina 三技术融合:毛白杨 740.2 Mb 二倍体解析基因组组装实战
  • Unity平面反射插件原理、优化与实战调优指南
  • 多模态模型成本优化:OpenRouter图像detail参数详解与实践
  • 扫描全能王 (CamScanner) v7.20.0.2606230000 高级版
  • STM32F103C8T6 软件I2C驱动SSD1306 OLED:3种常见初始化失败原因与修复
  • 阿里不做氢能制造商,要做产业‘操作系统提供商’
  • ARM开发板Linux内核移植实战:从源码编译到驱动适配的5个核心步骤
  • 手机端VLM部署实战:90M ONNX模型落地安卓全流程