主要思路:
看到t和n的数据范围就知道暴力是不可能的啦!
那暴力不行我们就二分:
先对数组进行一次排序;
然后从后往前扫,找到一个a[i]就进行两次二分操作:
第一次二分查找最大的up值,使得a[up]+a[i]<=r;
第二次二分查找最小的down值,使得l<=a[down]+a[i];
up-down的值就是a[i]组成的数对的个数。
关键来了,我当时已经想到了这个思路,但是花了足足50分钟竟然写不出来这个二分!!(;へ:)
后来,结束的时候去看看大哥们的代码,发现了这个东西:
ans+=upper_bound(a,a+i,r-a[i])-lower_bound(a,a+i,l-a[i]);
我当时人都傻了,这是什么玩意?!!!∑(゚Д゚ノ)ノ
后来我查了一下:
lower_bound( )和upper_bound( )都是利用二分查找的方法在一个排好序的数组中进行查找的。
在从小到大的排序数组中,
lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
(上面截取CSDN博主「brandong」的原创文章,原文链接:https://blog.csdn.net/qq_40160605/article/details/80150252)
哇,好东西,赶紧用起来,于是我也AC了
#pragma GCC optimize(2)
#include<map>
#include<queue>
#include<math.h>
#include<vector>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#define ll long long
#define IOS ios::sync_with_stdio(false);
#define f(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define rp(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(a,i) memset(a,i,sizeof(a));
#define die {cout<<"-1"; return 0;}
#define debug cout<<"0_0"<<endl
#define sd(a) scanf("%d",&a)
#define slld(a) scanf("%lld",&a)
#define sdd(a,b) scanf("%d %d",&a,&b)
const double eps=1e-8;
const ll INF=1e18;
const int max1=1e9;
const double pie=acos(-1.0);
using namespace std;
// ^_^
int a[200005];
int main()
{
int t;
sd(t);
while(t--)
{
int n,l,r;
sd(n);
sdd(l,r);
f(i,0,n-1)
sd(a[i]);
sort(a,a+n);
ll ans=0;
rp(i,n-1,0)
{
if(a[i]>=r)
continue;
if(a[i]>=l)
ans+=upper_bound(a,a+i,r-a[i])-a;
//从a[0]到a[i-1]中二分找到不超过 r-a[i]的最大下标
else
{
ans+=upper_bound(a,a+i,r-a[i])-lower_bound(a,a+i,l-a[i]);
}
}
printf("%lld\n",ans);
}
return 0;
}