📅  最后修改于: 2023-12-03 15:04:44.957000             🧑  作者: Mango
给定一个序列,每次查询一个区间[L,R]中有多少个元素的除数为奇数。
暴力枚举区间[L,R]中所有数,判断每个数的除数是否为奇数,时间复杂度O(N^2),无法通过本题。
因此,考虑优化。观察到本题查询区间内的元素,可以联想到使用线段树。
我们可以通过创建线段树,将每个区间[L,R]的除数为奇数的元素数量预处理出来,之后每次查询时直接返回区间内的值即可。
对于每个节点,我们只需要分别对左右儿子节点进行查询,然后将结果合并即可。对于叶子节点,只需要判断该节点的值是否为奇数即可。
时间复杂度为O(NlogN),可以通过本题。
以下代码为C++语言的实现代码:
#include<iostream>
#define int long long
using namespace std;
const int MAXN = 1e5 + 5;
int t[MAXN<<2], a[MAXN];
// 线段树的构建
void build(int k, int l, int r) {
if(l == r) {
t[k] = (a[l] % 2 == 1);
return;
}
int mid = (l + r)>>1;
build(k<<1, l, mid);
build(k<<1|1, mid + 1, r);
t[k] = t[k<<1] + t[k<<1|1];
}
// 线段树的查询函数
int query(int k, int l, int r, int ql, int qr) {
if(ql > qr)
return 0;
if(l >= ql && r <= qr)
return t[k];
int mid = (l + r)>>1;
int ans = 0;
if(ql <= mid)
ans += query(k<<1, l, mid, ql, qr);
if(qr > mid)
ans += query(k<<1|1, mid + 1, r, ql, qr);
return ans;
}
signed main() {
int n, m;
cin >> n >> m;
for(int i=1; i<=n; ++i)
cin >> a[i];
build(1, 1, n);
while(m--) {
int l, r;
cin >> l >> r;
cout << query(1, 1, n, l, r) << endl;
}
}
在进行线段树的查询时,我们需要将查询区间[L,R]在每个节点上与当前区间l~r的关系进行判断。 当当前区间完全包含[L,R]时,直接返回该区间的值。
若当前区间与[L,R]有交集,我们可以递归查询左右子节点并将结果进行合并。
如此逐渐递归,最终得到的结果即为[L,R]区间内除数为奇数的元素数量。
本题的做法是使用线段树进行区间查询。 线段树是一个重要的数据结构,我们在进行区间查询时可以使用线段树将问题的复杂度降为O(logN),非常高效。
对于这类区间查询问题,如果直接暴力枚举复杂度非常高,如果使用线段树,可以先对每个节点建立好对应的值,之后进行查询时直接返回对应区间的值即可。这样的复杂度也就很低了。