【线段树】区间gcd

这篇博客介绍了如何使用线段树解决区间gcd查询和修改的问题。在每次查询时,不仅需要维护每个区间的gcd,还要记录区间内不为gcd倍数的数的数量。当遇到gcd等于目标值x时,可以直接判断查询失败。文章提供了详细的题解思路和代码实现。

题意

初始有n个数。
有两个操作

  1. L R x 查询[L,R]的gcd是不是x,在查询过程中可以任意篡改一个数(不是真正的修改)
  2. pos x 将pos位置的值修改为x

题解

因为每次可以修改一个数,所以就不能只维护每个区间的gcd,还应该维护该区间不是x倍数的数的个数,如果 >= 2就失败。
这里有个优化就是对于 gcd == x的区别就没必要再查询下去了,因为不存在不是x倍数的区间。

代码

#include <bits/stdc++.h>
using namespace std;
#define FOR0(a,b) for(int i = a; i < b; ++i)
#define FORE(a,b) for(int i = a; i <= b; ++i)
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 5e5+5;
int c[maxn<<2];
int gcd(int a,int b) {return b == 0 ? a : gcd(b,a%b);}
void build(int l, int r, int rt) {
	if(l == r) {
		scanf("%d", &c[rt]);
		return;
	}
	int mid = (l+r)>>1;
	build(lson);
	build(rson);
	// cout << c[rt<<1] <<" " << c[rt<<1|1] << endl;
	c[rt] = gcd(c[rt<<1], c[rt<<1|1]);
}
void update(int L, int v,int l, int r, int rt) {
	if(l == r) {
		c[rt] = v;
		return;
	}
	int mid = (l+r)>>1;
	if(mid >= L) 
		update(L,v,lson);
	if(mid < L) 
		update(L,v,rson);
	c[rt] = gcd(c[rt<<1], c[rt<<1|1]);
}
int query(int L, int R,int x,int l, int r,int rt) {
	if(L > r || R < l) return 0;
	if(l == r) {
		return c[rt]%x != 0;
	}
	// cout << rt << endl;
	int ret = 0;
	int mid = (l+r)>>1;
	if(c[rt<<1]%x) {
		ret += query(L,R,x,lson);
		if(ret > 1) return 2;
	}
	if(c[rt<<1|1]%x) {
		ret += query(L,R,x,rson);
		if(ret > 1) return 2;
	}
	return ret;
}
int main() {
	int n;
	scanf("%d", &n);
	build(1,n,1);
	int q;
	scanf("%d",&q);
	int opt,l,r,v;
	for(int i = 0; i < q; ++i) {
		scanf("%d%d%d", &opt, &l, &r);
		if(opt == 1) {
			scanf("%d", &v);
			if(query(l,r,v,1,n,1)>1) puts("no");
			else puts("yes");
		} else {
			update(l,r,1,n,1);
		}
	}
	return 0;
}
### 区间最大公约数的C++实现 为了高效求解区间最大公约数问题,通常可以采用线段树(Segment Tree)结构。线段树是一种高效的数据结构,能够在对数组进行预处理后,以对数时间复杂度支持区间查询和单点更新操作。对于区间最大公约数问题,线段树的每个节点存储的是对应区间内所有元素的最大公约数。 最大公约数具有结合性但不具有可减性,因此不能使用前缀和的方式直接计算区间GCD。然而,线段树的结构允许将区间划分为若干个子区间,并分别计算这些子区间GCD,最终合并结果。 线段树中每个节点表示一个区间GCD。构建线段树时,从叶子节点开始递归构建,内部节点的值为左右子节点的GCD。对于查询操作,如果查询区间完全包含在当前节点的区间内,则返回该节点的GCD;否则递归查询左右子树并合并结果。 以下是一个完整的C++实现示例: ```cpp #include <iostream> #include <vector> #include <algorithm> #include <numeric> // for gcd using namespace std; class SegmentTree { private: vector<int> tree; int n; // 构建线段树 void build(int node, int l, int r, const vector<int>& arr) { if (l == r) { tree[node] = arr[l]; return; } int mid = (l + r) / 2; build(2 * node, l, mid, arr); build(2 * node + 1, mid + 1, r, arr); tree[node] = gcd(tree[2 * node], tree[2 * node + 1]); } // 查询区间GCD int query(int node, int l, int r, int ql, int qr) { if (ql > r || qr < l) return 0; // 区间不相交 if (ql <= l && r <= qr) return tree[node]; // 完全包含 int mid = (l + r) / 2; int left_gcd = query(2 * node, l, mid, ql, qr); int right_gcd = query(2 * node + 1, mid + 1, r, ql, qr); return gcd(left_gcd, right_gcd); } public: SegmentTree(const vector<int>& arr) { n = arr.size(); tree.resize(4 * n); build(1, 0, n - 1, arr); } int get_gcd(int ql, int qr) { return query(1, 0, n - 1, ql, qr); } }; int main() { vector<int> arr = {12, 24, 36, 48, 60}; SegmentTree st(arr); int l, r; cin >> l >> r; cout << "区间 [" << l << ", " << r << "] 的最大公约数是: " << st.get_gcd(l, r) << endl; return 0; } ``` 在上述代码中,`build`函数用于构建线段树,`query`函数用于查询指定区间的最大公约数。线段树的每个节点存储的是该区间GCD值。在查询过程中,如果查询区间与当前节点区间不相交,则返回0;如果完全包含当前节点区间,则返回该节点的GCD值;否则递归查询左右子树并合并结果。 此实现适用于静态数组的区间GCD查询。如果需要支持区间更新(如区间加法),则需要对线段树结构进行扩展,例如引入懒惰标记(Lazy Propagation)机制[^3]。 ### 区间最大公约数算法的优化与扩展 - **懒惰标记**:如果需要支持区间更新(如区间加法),可以引入懒惰标记来延迟更新子节点,从而提升效率。 - **多维扩展**:虽然线段树主要用于一维数组的区间查询,但也可以扩展到二维数组,实现矩形区域的最大公约数查询。 - **持久化线段树**:如果需要支持历史版本查询,可以使用持久化线段树结构,保留每次更新后的线段树状态。 通过上述方法,可以在C++中高效实现区间最大公约数的查询功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值