
2026-07-31:好子序列查询。用go言语,有个长度为 n 的整数数组 nums 和个给定的整数 p。界说种“好子序列”:从数组中收用至少个元素、但须至少铁心个元素(即弗成收用一起元素),保捏原有章程所得到的非空序列。如若这个序列中整个元素的大契约数碰劲等于 p林芝异型材设备价格,就称它为好子序列。
接下来会循序践诺 q 次修改操作,每次操作由对下标和数值构成,暗示将该下标位置的元素新为这个新数值。在每次新完成后,王人要判断面前数组是否包含至少个好子序列。
后请统计,在这 q 次查询新中,有若干次新之后数组是存在好子序列的,并复返这个次数。
2
1
1
queries[i] = [indi, vali]
1
0
输入: nums = [4,5,7,8], p = 3, queries = [[0,6],[1,9],[2,3]]。
输出: 2。
讲解:
i
[indi, vali]
操作
新后的 nums
是否存在好子序列
0
[0, 6]
将 nums[0] 新为 6
[6, 5, 7, 8]
否,因为不存在大契约数碰劲为 p = 3 的子序列
1
[1, 9]
将 nums[1] 新为 9
[6, 9, 7, 8]
是,子序列 [6, 9] 的大契约数碰劲为 p = 3
2
[2, 3]
将 nums[2] 新为 3
[6, 9, 3, 8]
是,子序列 [6, 9, 3] 的大契约数碰劲为 p = 3
因此,谜底是 2。
题目来独力扣3901。
算法总体想路
题目要求动态珍惜数组,在每次单点修改后判断是否存在个好子序列。
好子序列须餍足三个条目:
1. 非空;
2. 至少铁心个原数组元素(即弗成取一起);
3. 收用的元素保捏原章程(因为只顺心值,章程不影响 gcd,是以实质只需要商量哪些元素被选);
4. 整个收用元素的 大契约数(gcd)碰劲等于 p。
关节数学质
• 好子序列中每个元素王人须是 p 的倍数,不然 gcd 不可能等于 p。
• 若面前数组中整个 p 的倍数的 gcd 等于 p,何况数组中存在个非 p 的倍数,那么胜仗管用整个 p 的倍数、铁心大肆个非 p 的倍数,即可得到好子序列。
• 若面前数组中整个 p 的倍数的 gcd 等于 p,但数组中一起元素王人是 p 的倍数,则整个这个词数组的 gcd 等于 p,但整个这个词数组弗成被收用。此时需要判断是否存在删除个元素后,剩余 n-1 个数的 gcd 仍然等于 p。这是因为:如若存在职何个真子集的 gcd 等于 p,那么不错迟缓把其他元素加回想,终定存在个大小为 n-1 的子集 gcd 也等于 p,是以只需查验删除个数的情况。
• 反之,如若整个 p 的倍数的 gcd 不等于 p,则定不存在好子序列。
因此,算法中枢是用线段树动态珍惜整个 p 的倍数的集,快速得到它们的 gcd 以及个数。
预解决阶段
1. 输入数组 nums,长度 n,给定整数 p。
2. 创建线段树 SegTree,里面包含:
• tree[]:存储区间内整个 p 的倍数的 gcd(非 p 的倍数用 0 暗示,因为 gcd(0, x) = x,不影响缠绵);
• p:模数;
• cnt:面前数组中 p 的倍数的总个数。
3. 遍历 nums 的每个位置 i:
• 若 nums[i] p == 0,调用线段树的单点新函数,将该位置插入值 nums[i]。
• 每次新会珍惜叶子节点、新里面节点的 gcd,并同步加多 cnt。
4. 运事业态下,线段树根节点 tree[1] 即为面前整个 p 的倍数的 gcd。
每次查询新解决形式
关于每个查询 (idx, val),践诺以下操作:
1. 新数组值:将 nums[idx] 改为 val,同期调用线段树单点新 update(0, n-1, 1, idx, val)。
• 新函数会到达叶子节点 l == r:
• 先查验叶子面前存储的旧值(可能是 0 或原来的 p 的倍数),若旧值非 0 且为 p 的倍数,则 cnt--;
• 再查验新值 val,若 val p == 0,则 cnt++;
• 将叶子存储为 val(若为 p 的倍数)或 0(不然)。
• 回溯时从头缠绵里面节点的 gcd:tree[o] = gcd(tree[o*2], tree[o*2+1])。
2. 此时 nums 已新,cnt 正确林芝异型材设备价格,根节点 tree[1] 是面前整个 p 的倍数的 gcd。
3. 判断是否存在好子序列:
• 令 rtGcd = tree[1]。
• 如若 rtGcd != p,则面前数组不存在好子序列,谜底不加多。
• 如若 rtGcd == p,分两种情况:
• 情况:cnt != n
讲明数组中至少有个元素不是 p 的倍数。
此时收用整个 p 的倍数(个数 ≥ 1,因为 rtGcd == p 意味着存在 p 的倍数),再铁心个非 p 的倍数,这个子序列的 gcd 就等于 rtGcd = p,塑料挤出机且餍足“至少铁心个元素”,是以存在好子序列。谜底加 1。
• 情况二:cnt == n
讲明数组中整个元素王人是 p 的倍数,整个这个词数组的 gcd 即是 rtGcd = p,但整个这个词数组弗成行为子序列(须铁心至少个)。
因此需要判断是否存在个 真子序列 的 gcd 为 p。
证实前边的数学质,只需查验是否存在个元素被删除后,剩余 n-1 个元素的 gcd 仍然等于 p。
• 代码中有个化:若 n > 7,胜仗以为存在好子序列(这是个简化假定,实质可能存在反例,但按代码逻辑践诺)。
• 陈列要删除的下标 i(0 到 n-1);
• 缠绵除了 i 以外整个元素的 gcd;
• 若某轮缠绵得到 gcd == p,讲明存在好子序列,立即复返 true;
• 若整个删除尝试均失败,则不存在。
• 若 deleteOneCheck 复返 true,谜底加 1;不然不加。
4. 整个查询解决竣事后,复返累计的谜底个数。
技巧复杂度
• 线段树建立:运行通过 n 次单点新建立,每次新 O(log n),故 O(n log n)。
• 每次查询:次单点新 O(log n);判断和可能的查验:
• 统统有 q 次查询,因此总技巧复杂度为 O((n + q) log n)。
寥落空间复杂度
• 线段树数组大小为 4n,O(n)。
• 存储 nums 数组 O(n)。
• 递归调用栈度 O(log n)。
• 不商量输入查询 queries 的存储空间,寥落空间为 O(n)。
总结
本算法借助线段树珍惜“整个 p 的倍数”的 gcd 和个数,将动态判断好子序列的存在更动为对根节点 gcd 的分析,并期骗数学质简化了全 p 倍数情况的判断,全体复杂度足以支吾 n, q ≤ 50000 的数据界限。
Go好意思满代码如下:
.
package main
import "fmt"
// 欧几里得算法求大契约数
func gcd(a, b int) int {
for b != 0 {
a, b = b, ab
}林芝异型材设备价格
return a
}
// 线段树结构
type SegTree struct {
tree []int // 存储区间内整个p的倍数的gcd
p int // 模数
cnt int // 面前nums中p的倍数的个数
}
func NewSegTree(n, p int) *SegTree {
return &SegTree{
tree: make([]int, 4*n),
p: p,
cnt: 0,
}
}
// 单点新:将位置i的值改为val,同期珍惜cnt和tree
func (st *SegTree) update(l, r, o, i, val int) {
if l == r {
// 如若旧值正本是p的倍数,则cnt减1
if st.tree[o] != 0 && st.tree[o]st.p == 0 {
st.cnt--
}
// 如若新值是p的倍数,则cnt加1
if valst.p == 0 {林芝异型材设备价格
st.cnt++
}
// 存储值:p的倍数才确凿存储,不然视为0(不影响gcd)
if valst.p == 0 {
st.tree[o] = val
} else {
st.tree[o] = 0
}
return
}
m := (l + r) / 2
if i
st.update(l, m, o*2, i, val)
} else {
st.update(m+1, r, o*2+1, i, val)
}
// 新里面节点的gcd
st.tree[o] = gcd(st.tree[o*2], st.tree[o*2+1])
}
// 当nums中所罕有王人是p的倍数时,尝试删除个数,查验剩尾数的gcd是否等于p
func deleteOneCheck(nums []int, p int) bool {
n := len(nums)
for i := 0; i
curGcd := 0
for j := 0; j
if j == i {
continue
}
curGcd = gcd(curGcd, nums[j])
}
if curGcd == p {
return true
}
}
return false
}
// 主逻辑函数
func countGoodSubseq(nums []int, p int, queries [][]int) int {
n := len(nums)
st := NewSegTree(n, p)
// 运行建立:只将p的倍数插入线段树
for i, v := range nums {
if vp == 0 {
st.update(0, n-1, 1, i, v)
}
}
ans := 0
for _, q := range queries {
idx, val := q[0], q[1]
// 新线段树和nums数组
st.update(0, n-1, 1, idx, val)
nums[idx] = val林芝异型材设备价格
rtGcd := st.tree[1] // 根节点存储的是整个p的倍数的gcd
// 如若整个这个词数组整个p的倍数的gcd碰劲为p,则以为存在个好子序列
if rtGcd == p {
if st.cnt != n {
// 数组中不全是p的倍数
ans++
} else {
// 数组中全是p的倍数,需要寥落判断能否删除个数后使得剩余gcd=p
if n > 7 {
ans++
} else if deleteOneCheck(nums, p) {
ans++
}
}
}
}
return ans
}
func main {
nums := []int{4, 5, 7, 8}
p := 3
queries := [][]int{{0, 6}, {1, 9}, {2, 3}}
result := countGoodSubseq(nums, p, queries)
fmt.Println(result)
}
Python好意思满代码如下:
.
# -*-coding:utf-8-*-
import sys
# 欧几里得算法求大契约数
def gcd(a, b):
while b:
a, b = b, a b
return a
class SegTree:
def __init__(self, n, p):
self.n = n
self.p = p
self.tree = [0] * (4 * n) # 存储区间内整个p的倍数的gcd
self.cnt = 0 # 面前nums中p的倍数的个数
# 单点新:将位置i的值改为val,同期珍惜cnt和tree
def update(self, l, r, o, i, val):
if l == r:
# 如若旧值正本是p的倍数,则cnt减1
if self.tree[o] != 0 and self.tree[o] self.p == 0:
self.cnt -= 1
# 如若新值是p的倍数,则cnt加1
if val self.p == 0:
self.cnt += 1
# 存储值:p的倍数才确凿存储,不然视为0(不影响gcd)
self.tree[o] = val if val self.p == 0 else 0
return
m = (l + r) // 2
if i
self.update(l, m, o * 2, i, val)
else:
self.update(m + 1, r, o * 2 + 1, i, val)
# 新里面节点的gcd
self.tree[o] = gcd(self.tree[o * 2], self.tree[o * 2 + 1])
# 当nums中所罕有王人是p的倍数时,尝试删除个数,查验剩尾数的gcd是否等于p
def delete_one_check(nums, p):
n = len(nums)
for i in range(n):
cur_gcd = 0
for j in range(n):
if j == i:
continue
cur_gcd = gcd(cur_gcd, nums[j])
if cur_gcd == p:
return True
return False
def count_good_subseq(nums, p, queries):
n = len(nums)
st = SegTree(n, p)
# 运行建立:只将p的倍数插入线段树
for i, v in enumerate(nums):
if v p == 0:
st.update(0, n - 1, 1, i, v)
ans = 0
for idx, val in queries:
# 新线段树和nums数组
st.update(0, n - 1, 1, idx, val)
nums[idx] = val
rt_gcd = st.tree[1] # 根节点存储的是整个p的倍数的gcd
# 如若整个这个词数组整个p的倍数的gcd碰劲为p,则以为存在个好子序列
if rt_gcd == p:
if st.cnt != n:
# 数组中不全是p的倍数
ans += 1
else:
# 数组中全是p的倍数,需要寥落判断能否删除个数后使得剩余gcd=p
if n > 7:
ans += 1
elif delete_one_check(nums, p):
ans += 1
return ans
def main:
nums = [4, 5, 7, 8]
p = 3
queries = [[0, 6], [1, 9], [2, 3]]
result = count_good_subseq(nums, p, queries)
print(result)
if __name__ == "__main__":
main
C++好意思满代码如下:
.
#include
#include
using namespace std;
// 欧几里得算法求大契约数
int gcd(int a, int b) {
while (b != 0) {
int t = a b;
a = b;
b = t;
}
return a;
}
// 线段树类
class SegTree {
public:
vector tree; // 存储区间内整个 p 的倍数的 gcd
int p; // 模数
int cnt; // 面前 nums 中 p 的倍数的个数
SegTree(int n, int p_) : p(p_), cnt(0) {
tree.assign(4 * n + 5, 0);
}
// 单点新:将位置 i 的值改为 val,同期珍惜 cnt 和 tree
void update(int l, int r, int o, int i, int val) {
if (l == r) {
int old = tree[o]; // 旧值(0 暗示非 p 的倍数)
// 如若旧值正本是 p 的倍数,则 cnt 减 1
if (old != 0 && old p == 0) {
cnt--;
}
// 如若新值是 p 的倍数,则 cnt 加 1
if (val p == 0) {
cnt++;
}
// 存储值:p 的倍数才确凿存储,不然视为 0(不影响 gcd)
tree[o] = (val p == 0) ? val : 0;
return;
}
int m = (l + r) / 2;
if (i
update(l, m, o * 2, i, val);
} else {
update(m + 1, r, o * 2 + 1, i, val);
}
// 新里面节点的 gcd
tree[o] = gcd(tree[o * 2], tree[o * 2 + 1]);
}
};
// 当 nums 中所罕有王人是 p 的倍数时,尝试删除个数,查验剩尾数的 gcd 是否等于 p
bool deleteOneCheck(const vector& nums, int p) {
int n = (int)nums.size;
for (int i = 0; i
int curGcd = 0;
for (int j = 0; j
if (j == i) continue;
curGcd = gcd(curGcd, nums[j]);
}
if (curGcd == p) {
return true;
}
}
return false;
}
// 主逻辑函数
int countGoodSubseq(vector& nums, int p, const vector>& queries) {
int n = (int)nums.size;
SegTree st(n, p);
// 运行建立:只将 p 的倍数插入线段树
for (int i = 0; i
if (nums[i] p == 0) {
st.update(0, n - 1, 1, i, nums[i]);
}
}
int ans = 0;
for (const auto& q : queries) {
int idx = q[0], val = q[1];
// 新线段树和 nums 数组
st.update(0, n - 1, 1, idx, val);
nums[idx] = val;
int rtGcd = st.tree[1]; // 根节点存储的是整个 p 的倍数的 gcd
// 如若整个这个词数组整个 p 的倍数的 gcd 碰劲为 p,则以为存在个好子序列
if (rtGcd == p) {
if (st.cnt != n) {
// 数组中不全是 p 的倍数
ans++;
} else {
// 数组中全是 p 的倍数,需要寥落判断能否删除个数后使得剩余 gcd = p
if (n > 7) {
ans++;
} else if (deleteOneCheck(nums, p)) {
ans++;
}
}
}
}
return ans;
}
int main {
vector nums = {4, 5, 7, 8};
int p = 3;
vector> queries = {{0, 6}, {1, 9}, {2, 3}};
int result = countGoodSubseq(nums, p, queries);
cout
return 0;
}
·
咱们服气东谈主工智能为肤浅东谈主提供了种“增强用具”,并勉力于共享全位的AI学问。在这里,您不错找到新的AI科普著述、用具评测、擢升率的心事以及行业细察。
接待关注“福大大架构师逐日题”,发音问可获取口试而已,让AI助力您的翌日发展。Q Q:183445502相关词条:管道保温施工 塑料挤出设备 预应力钢绞线 玻璃棉厂家 保温护角专用胶
1.本网站以及本平台支持关于《新广告法》实施的“极限词“用语属“违词”的规定,并在网站的各个栏目、产品主图、详情页等描述中规避“违禁词”。
2.本店欢迎所有用户指出有“违禁词”“广告法”出现的地方,并积极配合修改。
3.凡用户访问本网页,均表示默认详情页的描述林芝异型材设备价格,不支持任何以极限化“违禁词”“广告法”为借口理由投诉违反《新广告法》,以此来变相勒索商家索要赔偿的违法恶意行为。
Powered by 塑料挤出机厂_建仓机械 RSS地图 HTML地图
Copyright Powered by365站群 © 2025-2035