📌  相关文章
📜  Q 范围查询的数组中偶数和三元组的计数(1)

📅  最后修改于: 2023-12-03 14:46:48.946000             🧑  作者: Mango

题目描述

给定长度为n的数组a,和q个询问,对于每个询问,给定两个整数l和r,求出[l, r]区间内偶数的个数和这些偶数可以组成的三元组的个数(三元组(a, b, c)满足l ≤ a < b < c ≤ r)。

解法思路
第一步

考虑先把数组a中的所有偶数的下标保存下来,这样就可以做到O(1)时间判断一个数是否为偶数。

vector<int> evenIndex;
for (int i = 0; i < n; i++) {
    if (a[i] % 2 == 0) {
        evenIndex.push_back(i);
    }
}
第二步

接下来考虑如何求出[l, r]区间内偶数的个数。由于偶数的下标已经保存,可以通过二分查找找到区间[l, r]左右端点在偶数下标中的位置,做差即可得到区间内偶数的个数。

int lIdx = lower_bound(evenIndex.begin(), evenIndex.end(), l) - evenIndex.begin();
int rIdx = upper_bound(evenIndex.begin(), evenIndex.end(), r) - evenIndex.begin() - 1;
int evenCnt = rIdx - lIdx + 1;
第三步

最后考虑如何求出[l, r]区间内可以组成的三元组的个数。由于三元组(a, b, c)满足l ≤ a < b < c ≤ r,所以a的范围是[l, r-2],b的范围是[a+1, r-1],c的范围是[b+1, r]。但是由于只需求偶数的三元组个数,所以只需要考虑偶数在a, b, c中的位置即可。假设a, b, c下标分别为aIdx, bIdx, cIdx,则有:

  1. aIdx的范围是[l, r-2],对应的偶数个数为evenCnt1。
  2. bIdx的范围是[aIdx+1, r-1],对应的偶数个数为evenCnt2。
  3. cIdx的范围是[bIdx+1, r],对应的偶数个数为evenCnt3。

假设evenIdx数组是按升序排列的,则可以先对evenIdx数组做一次离散化,得到revEvenIdx数组,其中revEvenIdx[i]表示原数组下标为evenIdx[i]的值,然后就可以通过二分查找找到a, b, c对应的偶数下标,再根据区间和公式sum[right] - sum[left-1]求和即可。

vector<int> revEvenIdx;
map<int, int> mp;
for (int i = 0; i < evenIndex.size(); i++) {
    mp[evenIndex[i]] = i+1;
    revEvenIdx.push_back(evenIndex[i]);
}

long long int ans = 0;
for (int aIdx = lIdx; aIdx <= rIdx-2; aIdx++) {
    int evenCnt1 = aIdx - lIdx + 1;
    int bIdxLb = lower_bound(evenIndex.begin()+aIdx+1, evenIndex.end(), revEvenIdx[aIdx]+1) - evenIndex.begin();
    int bIdxUb = upper_bound(evenIndex.begin()+aIdx+1, evenIndex.end(), r) - evenIndex.begin() - 1;
    int evenCnt2 = bIdxUb - bIdxLb + 1;
    if (evenCnt2 == 0) {
        continue;
    }
    int cIdxLb = lower_bound(evenIndex.begin()+bIdxLb, evenIndex.end(), revEvenIdx[bIdxUb]+1) - evenIndex.begin();
    int cIdxUb = rIdx;
    int evenCnt3 = cIdxUb - cIdxLb + 1;
    if (evenCnt3 == 0) {
        continue;
    }
    long long int cnt = (long long int)evenCnt1 * evenCnt2 * evenCnt3;
    ans += cnt;
}
总时间复杂度

对于每个询问的时间复杂度为O(log n + qlog n),其中O(log n)用于二分查找查找区间内偶数个数,q次查找共需O(qlog n)时间。

完整代码
vector<int> evenIndex; //存储偶数下标
map<int, int> mp; //离散化偶数下标
vector<int> revEvenIdx; //逆离散化偶数下标

int main() {
    int n, q;
    scanf("%d%d", &n, &q);
    for (int i = 0; i < n; i++) {
        int x;
        scanf("%d", &x);
        if (x % 2 == 0) {
            evenIndex.push_back(i);
        }
    }
    for (int i = 0; i < evenIndex.size(); i++) {
        mp[evenIndex[i]] = i+1;
        revEvenIdx.push_back(evenIndex[i]);
    }
    while (q--) {
        int l, r;
        scanf("%d%d", &l, &r);
        int lIdx = lower_bound(evenIndex.begin(), evenIndex.end(), l) - evenIndex.begin();
        int rIdx = upper_bound(evenIndex.begin(), evenIndex.end(), r) - evenIndex.begin() - 1;
        int evenCnt = rIdx - lIdx + 1;
        long long int ans = 0;
        for (int aIdx = lIdx; aIdx <= rIdx-2; aIdx++) {
            int evenCnt1 = aIdx - lIdx + 1;
            int bIdxLb = lower_bound(evenIndex.begin()+aIdx+1, evenIndex.end(), revEvenIdx[aIdx]+1) - evenIndex.begin();
            int bIdxUb = upper_bound(evenIndex.begin()+aIdx+1, evenIndex.end(), r) - evenIndex.begin() - 1;
            int evenCnt2 = bIdxUb - bIdxLb + 1;
            if (evenCnt2 == 0) {
                continue;
            }
            int cIdxLb = lower_bound(evenIndex.begin()+bIdxLb, evenIndex.end(), revEvenIdx[bIdxUb]+1) - evenIndex.begin();
            int cIdxUb = rIdx;
            int evenCnt3 = cIdxUb - cIdxLb + 1;
            if (evenCnt3 == 0) {
                continue;
            }
            long long int cnt = (long long int)evenCnt1 * evenCnt2 * evenCnt3;
            ans += cnt;
        }
        printf("%d %lld\n", evenCnt, ans);
    }
    return 0;
}