📅  最后修改于: 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,则有:
假设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;
}