题目描述
难度分:1300
输入T(≤104)表示T组数据。所有数据的n之和≤2×105。
每组数据输入n(1≤n≤2×105),L,R(1≤L≤R≤109)和长为n的数组a(1≤a[i]≤109)。
输出有多少对(i,j)满足i<j且L≤a[i]+a[j]≤R。
输入样例
4
3 4 7
5 1 2
5 5 8
5 1 2 4 3
4 100 1000
1 1 1 1
5 9 13
2 5 5 1 1
输出样例
2
7
0
1
算法
二分查找
做完之后感觉好像可以用双指针,但就算用双指针也需要先对数组排序,并不影响整体的时间复杂度,所以还是不写了。
依题意很容易认识到一点“i和j谁是谁根本无所谓”,所以为了更方便,直接对数组先排个序,然后枚举那个较小值,假设为a[i]。对于每个a[i],在它右边二分查找到第一个满足a[i]+a[r]>R的位置r,以及第一个满足a[i]+a[l]≥L的位置l,所有j∈[l,r)的元素a[j]都满足a[i]+a[j]∈[L,R],能够贡献r−l个数对,把所有i的贡献累加起来就是答案。
复杂度分析
时间复杂度
排序的时间复杂度为O(nlog2n)。枚举i的时间复杂度为O(n),对每个i二分查找范围的时间复杂度为O(log2n)。因此,整个算法的时间复杂度为O(nlog2n)。
空间复杂度
除了排序之外只使用了有限几个变量,而以快速排序为基准,额外空间复杂度为O(log2n)。
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 200010;
int t, n, L, R, a[N];
void solve() {
sort(a + 1, a + n + 1);
long long ans = 0;
for(int i = 1; i <= n; i++) {
int l = lower_bound(a + i + 1, a + n + 1, L - a[i]) - a;
int r = upper_bound(a + i + 1, a + n + 1, R - a[i]) - a;
ans += r - l;
}
printf("%lld\n", ans);
}
int main() {
scanf("%d", &t);
while(t--) {
scanf("%d%d%d", &n, &L, &R);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
solve();
}
return 0;
}