题意
题目链接
给你一个有
n
n
n个整数的数组
a
a
a,当
1
≤
i
≤
j
≤
n
1≤i≤j≤n
1≤i≤j≤n时,求所有满足
l
≤
a
i
+
a
j
≤
r
l≤a_{i}+a_{j}≤r
l≤ai+aj≤r的
(
i
,
j
)
(i,j)
(i,j)的对数。所有测试样例中
n
n
n的总和满足
∑
n
≤
2
e
5
\sum{n}≤2e5
∑n≤2e5。
思路
1.先对数组中的数从小到大排序。
2.对于从小到大的第
i
i
i个数,对第
i
+
1
i+1
i+1到
n
n
n个数二分,分别找出最小的满足条件的边界和最大的满足条件的边界。
3.最大边界与最小边界位置的差,再加1,就是当前的对数。
4.遍历计算所有对数的总和,就是答案。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 2e5+5;
ll a[maxn];
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
ll t;cin>>t;
while(t--){
ll n,l,r;
cin>>n>>l>>r;
for(ll i=1;i<=n;i++)cin>>a[i];
sort(a+1,a+1+n);
ll ans = 0;
for(ll i=1;i<n;i++){
ll left = i+1, right = n, pos_l = -1, pos_r = -1;
while(left<=right){
ll mid = (left+right)>>1;
if(a[mid]+a[i]<l)left = mid+1;
else if(a[mid]+a[i]>=l)right = mid-1, pos_l=mid;
}
left = i+1, right = n;
while(left<=right){
ll mid = (left+right)>>1;
if(a[mid]+a[i]<=r)left = mid+1,pos_r=mid;
else if(a[mid]+a[i]>r)right = mid-1;
}
if(pos_l!=-1&&pos_r!=-1){
if(pos_l<=pos_r){
ans+=(pos_r-pos_l+1);
}
}
}
cout<<ans<<endl;
}
return 0;
}
总结
在二分时,需要注意只有满足条件才记录当前位置,而且只要当前满足条件,就要继续寻找,而不能停止寻找。直到继续寻找之后无法满足条件,再终止循环。