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

Kimi LeetCode 3901. 好子序列查询 Rust实现

根据搜索结果,LeetCode 3901「好子序列查询」的核心思路是:线段树 + GCD。下面给出 Rust 实现。

---

思路

1. 只关心 p 的倍数:如果 `nums[i]` 不是 `p` 的倍数,它对 GCD 为 `p` 的子序列没有贡献,在线段树中视为 `0`。
2. 线段树维护所有 p 倍数的 GCD:记为 `g`。
- 若 `g != p`,则不存在好子序列。
- 若 `g == p`,则所有 p 的倍数整体 GCD 已是 `p`。
3. 长度严格小于 n:
- 若 `cnt < n`(并非所有元素都是 p 的倍数),直接取所有 p 的倍数即可,长度必然 `< n`。
- 若 `cnt == n`(所有元素都是 p 的倍数),需要删一个元素后 GCD 仍为 `p`。
- 关键结论:若 `n > 6` 且整体 GCD 为 `p`,则一定可以删一个元素使 GCD 仍为 `p`。因此只需在 `n <= 6` 时暴力枚举删除位置。
4. 线段树操作:单点更新、区间查询 GCD。

---

Rust 实现

```rust
use std::cmp::min;

struct SegTree {
n: usize,
tree: Vec<i32>,
}

impl SegTree {
fn new(n: usize) -> Self {
Self {
n,
tree: vec![0; n * 4],
}
}

fn build(&mut self, u: usize, l: usize, r: usize, arr: &[i32]) {
if l == r {
self.tree[u] = arr[l - 1];
return;
}
let mid = (l + r) >> 1;
self.build(u << 1, l, mid, arr);
self.build(u << 1 | 1, mid + 1, r, arr);
self.tree[u] = Self::gcd(self.tree[u << 1], self.tree[u << 1 | 1]);
}

fn modify(&mut self, u: usize, l: usize, r: usize, x: usize, v: i32) {
if l == r {
self.tree[u] = v;
return;
}
let mid = (l + r) >> 1;
if x <= mid {
self.modify(u << 1, l, mid, x, v);
} else {
self.modify(u << 1 | 1, mid + 1, r, x, v);
}
self.tree[u] = Self::gcd(self.tree[u << 1], self.tree[u << 1 | 1]);
}

fn query(&self, u: usize, l: usize, r: usize, ql: usize, qr: usize) -> i32 {
if ql > qr {
return 0;
}
if ql <= l && r <= qr {
return self.tree[u];
}
let mid = (l + r) >> 1;
if qr <= mid {
return self.query(u << 1, l, mid, ql, qr);
}
if ql > mid {
return self.query(u << 1 | 1, mid + 1, r, ql, qr);
}
Self::gcd(
self.query(u << 1, l, mid, ql, mid),
self.query(u << 1 | 1, mid + 1, r, mid + 1, qr),
)
}

fn gcd(mut a: i32, mut b: i32) -> i32 {
while b != 0 {
let t = a % b;
a = b;
b = t;
}
a
}
}

impl Solution {
pub fn count_good_subseq(nums: Vec<i32>, p: i32, queries: Vec<Vec<i32>>) -> i32 {
let n = nums.len();
let mut nums = nums;
let mut seg = SegTree::new(n);

// 初始化线段树:p 的倍数保留原值,否则为 0
let mut init = vec![0; n];
let mut cnt = 0;
for i in 0..n {
if nums[i] % p == 0 {
init[i] = nums[i];
cnt += 1;
}
}
if n > 0 {
seg.build(1, 1, n, &init);
}

let mut ans = 0;
for q in queries {
let idx = q[0] as usize;
let val = q[1];

// 移除旧值
if nums[idx] % p == 0 {
seg.modify(1, 1, n, idx + 1, 0);
cnt -= 1;
}
// 加入新值
if val % p == 0 {
seg.modify(1, 1, n, idx + 1, val);
cnt += 1;
}
nums[idx] = val;

// 整体 GCD 不是 p,不可能有好子序列
if seg.tree[1] != p {
continue;
}

// 不是全部元素都是 p 的倍数,或 n > 6
if cnt < n || n > 6 {
ans += 1;
continue;
}

// n <= 6 且所有元素都是 p 的倍数,暴力枚举删除一个位置
let mut ok = false;
for i in 1..=n {
let left_g = seg.query(1, 1, n, 1, i.saturating_sub(1));
let right_g = seg.query(1, 1, n, i + 1, n);
if SegTree::gcd(left_g, right_g) == p {
ok = true;
break;
}
}
if ok {
ans += 1;
}
}

ans
}
}
```

---

复杂度

- 时间复杂度:O((n + q) \times \log n),其中 n \le 6 时的暴力枚举是常数级。
- 空间复杂度:O(n)。

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

相关文章:

  • RAG 知识库交付实战(下):18 条用例与成本测算——质量证明与进化蓝图
  • Swagger UI 与文档发布:从接口注释到在线文档的完整实践
  • CRC8 循环冗余校验算法详解
  • 破解物理AI技术困局(50):TVA层级强化学习架构
  • 华为OD机试真题 新系统 2026-08-05 JavaGoC 实现【IPv4等长子网划分与自动分配系统】
  • Win11Debloat实测:半小时卸载预装软件、关闭遥测,新电脑终于不卡了
  • SV学习记录(五)
  • 告别传统流量竞价,拓氪科技如何依托GEO优化构建AI时代出海品牌信任底座?
  • 零成本让GIMP变成Photoshop界面:PhotoGIMP完整上手指南
  • 2026年地坪漆十大品牌厂家深度盘点:正规合规服务商选型指南、场景适配解析及合作避坑全维度实用FAQ - 行业观察网
  • ACTH (34-39) ;AFPLEF
  • PVEL-AD光伏缺陷检测实战指南:从0到1构建工业级EL质检系统
  • 055、去马赛克的“伪彩色诅咒“——方向插值/残差插值/深度学习方法的伪色抑制能力对比及在红色高光区域的实战表现
  • 13:eBPF 崛起——从一个包过滤器到一个完整的操作系统的可编程平台
  • SAP Gateway Task Gateway 中 Software Version 的配置原理与 Provider 路由机制
  • 2026智慧商业大数据可视化大屏推荐,企业该怎么选? - 选型|行业|价格|案例
  • LeetCode 1004:最大连续1的个数 III(滑动窗口) —— 题解
  • 老板一句“这个月线索为什么下降”,智能问数能查到什么?
  • 行业板块轮动因子实战从板块资金到因子建模的本地化Python全流程
  • Socket编程:客户端与服务器通信全解析(网络编程)
  • 代理(静态和动态)
  • 2026年消防设施操作员证报名入口,正规报考中心报名通道汇总 - 中科资质认证报考中心
  • 硬件测试内容之十三:LDO(芯片)
  • 郑州考公党必看!大学生毕业后档案存放流程!不用线下跑! - 实时传讯
  • 完整入门vscode-mermaid-preview:3步实现Mermaid图表实时预览与高清导出
  • 不同传感器前中后融合方案简介
  • 2026 年太原空调加氟空调出售,中央空调维修怎么预约? - LYL仔仔
  • Kettle数据迁移全复盘:从旧系统到新平台的8条实战经验清单
  • 手把手教你学 Simulink—— 整流器电磁干扰(EMI)滤波器设计与传导骚扰仿真
  • 【AI智能体速通】05.Agentic AI