D. Counting Pairs
题目:
中文翻译 :
D. 计数对
每个测试的时间限制为2秒 每个测试的内存限制为256兆字节 给定一个由n个整数组成的序列a,其中序列的第i个元素等于ai。同时给定两个整数x和y(x≤y)。
如果整数对(i,j)满足以下条件,则认为该对是有趣的:
1≤i<j≤n; 如果同时从序列a中移除位置i和j的元素,剩余元素的和至少为x且至多为y。 你的任务是确定给定序列a中有趣整数对的数量。
输入 第一行包含一个整数t(1≤t≤10^4)——测试用例的数量。
每个测试用例包含两行:
第一行包含三个整数n,x,y(3≤n≤2⋅10^5,1≤x≤y≤2⋅10^14); 第二行包含n个整数a1,a2,…,an(1≤ai≤10^9)。 输入的额外约束:所有测试用例中n的总和不超过2⋅10^5。
输出 对于每个测试用例,输出一个整数——给定序列a中有趣整数对的数量。
思路 :
我们可以用 brute force 即暴力的方法,枚举二元组 (i,j)i < j 。然而时间复杂度是O(n*n),
会出现 tle 的情况 。那么怎么将他优化 ?
设sum 表示 arry 的和 , 那么要解决的问题就是 x <= sum-ai-aj <= y ,我们可以一维的枚举
ai , 在知道ai 的情况下,我们要寻找相应符合的 aj ,可以将x <= sum-ai-aj <= y 改写为 :
sum-y-ai <= aj <= sum-ai-x ,那么我们的任务是查找符合上试的 aj ,关于查找,一种很常见的优化时间的方法是 binery search ! (二分) 时间复杂度是 O(log n ),但是我们用二分之前需要先构造单调性,我们可以很容易观察到: 符合条件的 i,j ,与其相对应相对位置无关,所以我们可以用 sort 进行排序 ( 复杂度 :O(n*log n )), 然后遍历 数组 枚举 ai,寻找aj的lower bound 和upper bound ,但是要注意 寻找的时候 要保证 i < j , 所以找出上下界之后还要一些处理。
C++ ACcode :
#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,n,x,y;
signed main()
{
ios::sync_with_stdio(0),cin.tie(0);
cin>>t;
auto solve = [&](){
cin>>n>>x>>y; // x <= y
vector<int> a(n+100);
int s=0;
for(int i=1;i<=n;++i){
cin>>a[i];
s+=a[i];
}
sort(a.begin()+1,a.begin()+1+n);
auto aj_r = [&](int xx){
/* wa point 1 and debug :
variation x is using for public ,
but perviously I created
a variatione and the name is same with x ,
leading to ambiguous*/
int l=1,r=n,res=0;
while(l<=r){
int mid=(l+r)>>1;
if(a[mid]<=s-x-a[xx]){
res=mid;
l=mid+1;
}
else r=mid-1;
}
return res;
};
auto aj_l = [&](int xx){
int l=1,r=n,res=0;
while(l<=r){
int mid=(l+r)>>1;
if(a[mid]>=s-y-a[xx]){
res=mid;
r=mid-1;
}
else l=mid+1;
}
return res;
};
int ans=0; /* we should avoid to repreatly chose ai ,and aj,
since i<j ,and ai<=aj ,so we should pay attention to
determain the lowerbound --> l; */
for(int i=1;i<=n;++i){
int l=aj_l(i);
int r=aj_r(i);
/* cout<<"------"<<'\n';
cout<<x<<"<="<<s-a[i]-a[l]<<"<="<<y<<'\n';
cout<<x<<"<="<<s-a[i]-a[r]<<"<="<<y<<'\n';
cout<<"-----"<<'\n';
if(l==i) l++,cout<<"ca0"<<'\n'; */
l= i>=l? i+1:l; // detail should be pay more attention to !
if(l==0||r==0||r<=i||l>r) continue ;
/* wa point 2 ans debug :
why could not without this if(empression) ? ? ? ? ? */
int sl=s-a[i]-a[l];
int sr=s-a[i]-a[r];
if(!(x<=sl&&sl<=y)&&!(x<=sr&&sr<=y)) continue ;
/* why shound add this (if mantain) to keep the ans ? ? ? ?
cout<<"------"<<'\n';
cout<<x<<"<="<<s-a[i]-a[l]<<"<="<<y<<'\n';
cout<<x<<"<="<<s-a[i]-a[r]<<"<="<<y<<'\n';
cout<<i<<" "<<l<<" "<<r<<'\n';
cout<<"-----"<<'\n';
cout<<"+"<<r-l+1<<'\n'; */
ans+=r-l+1;
}
cout<<ans<<'\n';
};
while(t--) solve();
return 0;
}