题目链接
https://codeforces.com/problemset/problem/1538/C
题目描述
【题目描述】
给出一个由整数组成的数组 a a a,求一对整数 ( i , j ) (i, j) (i,j)( 1 ≤ i ≤ j ≤ n 1\le i \le j \le n 1≤i≤j≤n)满足 l ≤ a i + a j ≤ r l \le a_i + a_j \le r l≤ai+aj≤r 的数量。
【输入格式】
在输入的第一行为一个整数 t t t( 1 ≤ t ≤ 10 4 1 \le t \le {10}^4 1≤t≤104),为数据组数。
接下来对于每组数据,第一行为三个整数 n , l , r n,l,r n,l,r( 1 ≤ n ≤ 2 × 10 5 1 \le n \le 2 \times {10}^5 1≤n≤2×105, 1 ≤ l ≤ r ≤ 10 9 1 \le l \le r \le {10}^9 1≤l≤r≤109),为数组的长度和上文中的 l , r l, r l,r。第二行有 n n n 个整数 a 1 , a 2 , … , a n a_1, a_2, \ldots , a_n a1,a2,…,an( 1 ≤ a i ≤ 10 9 1 \le a_i \le {10}^9 1≤ai≤109)表示数组 a a a。
保证对于所有组数据 ∑ n ≤ 2 × 10 5 \sum n \le 2 \times {10}^5 ∑n≤2×105。
【输出格式】
对于每组数据,输出满足条件的 ( i , j ) (i,j) (i,j) 组数。
解题思路
- 首先把数组从小到大排序。
- 对于第 i i i个数,在序列 a 1 a_1 a1到 a i − 1 a_{i-1} ai−1找到最大的一个数 a k a_k ak使得 a k + a i ≤ r a_k+a_i \le r ak+ai≤r.很明显,使用二分查找即可;同样的,在序列 a 1 a_1 a1到 a i − 1 a_{i-1} ai−1找到最小的一个数 a k a_k ak使得 a k + a i ≥ l a_k+a_i \geq l ak+ai≥l.也是使用二分查找;
- 遍历每个位置,即可统计出所有满足条件的所有数对。
参考代码
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
const int MAXN=200005;
int a[MAXN];
int t,n,l,r;
int SearchLeft(int left, int right, int x){
int res=0, mid;
while( left <= right ) {
mid=(left+right)>>1;
if( a[mid]+a[x] >= l ) {
res=mid;
right=mid-1;
}
else left=mid+1;
}
return res;
}
int SearchRight(int left, int right, int x){
int res=0, mid;
while( left <= right ) {
mid=(left+right)>>1;
if( a[mid]+a[x] <= r ) {
res=mid;
left=mid+1;
}
else right=mid-1;
}
return res;
}
int main(){
scanf("%d", &t);
while( t-- ) {
int left,right;
long long ans=0;
scanf("%d%d%d", &n, &l, &r);
for(int i=1; i<=n; i++)
scanf("%d", &a[i]);
sort(a+1,a+1+n);
for(int i=n; i>=2; i--) {
left=SearchLeft(1,i-1,i);
right=SearchRight(1,i-1,i); // printf("left:%d right:%d\n",left,right);
if( left==0 || right==0 )
continue;//break;是错的
ans += right-left+1;
}
printf("%lld\n",ans);
}
return 0;
}