区间最大公约数 用线段树实现:区间加+区间公共gcd查询·

题目:

6fa6bae0ffc4421d94e7bb4edc8fc0e8.png

题解:

将元素组的差分数组存入线段树

区间加操作:

将元素组的差分数组存入线段树,在每一次对区间[ l, r ]加操作时等价于在线段树中的两次单点修改操作:对d[l]+c和d[r+1]-c。

求区间最大公约数:

这里用了一个数学知识两个数的最大公约数和两个数的一个组合是一样的。

因为线段树的每个节点代表的是原数组中对应因为和前一位的一个组合,所以在直接查询一个区间[ l , r ]的最大公约数时真正返回的是 元素组中区间[ l-1 , r ]的最大公约数,所以线段树还要存一个前缀和表示线段树中当前这位在元素中中是多少,这样在查询区间[ l , r ]的最大公约数时,就可以准确地表示为gcd( sum(l) , [ l+1 , r ] ) .

代码:

#include<bits/stdc++.h>
using namespace std;

const int N=500010;

#define int long long

int n,m;

int w[N];

struct Node{
    int l,r;
    int sum,d;
}tr[N*4];

int gcd(int a,int b){
    return b?gcd(b,a%b):a;
}

void pushup(Node& u,Node& l,Node& r){
    u.sum&#
### 高效计算数组区间 [L, R] 内元素的最大公因数 (GCD) 为了高效处理此类查询,一种常用的方法是利用 **线段树** 或者 **稀疏表 (Sparse Table)** 来预处理数据并支持快速查询。 #### 使用线段树 线段树是一种二叉树形的数据结构,在节点上存储特定区间的聚合信息。对于 GCD 查询来说,每个节点保存对应子数组的 GCD 值。构建完成后,可以在 O(log n) 时间内完成任意区间上的 GCD 计算。 ```cpp struct SegmentTree { int size; vector<int> tree; void init(int _size) { // 初始化函数 size = _size; tree.resize(4 * size); } int gcd(int a, int b) const { // 辅助函数:求两个整数的最大公约数 return !b ? a : gcd(b, a % b); } void build(const vector<int>& array, int node, int start, int end) { // 构建线段树 if (start == end) { tree[node] = array[start]; } else { int mid = (start + end) / 2; build(array, 2 * node, start, mid); // 左孩子 build(array, 2 * node + 1, mid + 1, end);// 右孩子 tree[node] = gcd(tree[2 * node], tree[2 * node + 1]); } } int query(int l, int r, int node=1, int start=0, int end=-1) { // 区间查询 if(end==-1){ end=size-1; } if(l > end || r < start){return 0;} // 完全不重叠区域返回无穷大 if(l <= start && end <= r){return tree[node];} // 当前范围完全位于询问范围内 int mid=(start+end)/2; // 否则递归到左右儿子去查找 return gcd(query(l,r,node*2,start,mid),query(l,r,node*2+1,mid+1,end)); } }; ``` 此方法允许在线段树初始化阶段花费 O(n log n) 的时间成本来预先计算所有可能的连续子序列的最大公约数值,并能在之后每次查询时仅需 O(log n)[^1] 的开销即可获得结果。 #### 使用 Sparse Table 另一种更高效的解决方案是采用稀疏表技术。这种方法通过提前计算出长度为 \(2^k\) 的所有子串的最大公约数来进行速。一旦完成了这些准备工作,则任何给定区间最大公约数都可以在常量时间内得出。 ```cpp class SparseTable { public: int n; vector<vector<int>> st; SparseTable(vector<int>& nums):n(nums.size()),st(n,vector<int>(log2(n)+1)){ for(int i=0;i<n;++i)st[i][0]=nums[i]; for(int j=1;(1<<j)<=n;j++) for(int i=0;i+(1<<j)-1<n;i++) st[i][j]=__gcd(st[i][j-1],st[i+(1<<(j-1))][j-1]); } int get_gcd(int L,int R){ int k=log2(R-L+1); return __gcd(st[L][k],st[R-(1<<k)+1][k]); } }; ``` 上述代码实现了基于稀疏表的 GCD 查询功能。`get_gcd(L,R)` 函数能够在 O(1) 时间复杂度下获取指定区间 `[L..R]` 上的最大公约数[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值