📌  相关文章
📜  Q查询的索引范围为[L,R]的除数为奇数的元素计数(1)

📅  最后修改于: 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),非常高效。

对于这类区间查询问题,如果直接暴力枚举复杂度非常高,如果使用线段树,可以先对每个节点建立好对应的值,之后进行查询时直接返回对应区间的值即可。这样的复杂度也就很低了。