Codeforces Gym 101064D
题目链接:http://vjudge.net/problem/Gym-101064D
题目大意:给出一个长度为\(n\)的数组,求集合\((a_i+a_j) | i<j\)里的第\(k\)大元素(\(n\leq100000\))
分析:
朴素的做法时间复杂度为\(O(n^2)\),难以承受,因此不难想到二分答案,每次统计在所有二元组中和小于它的个数,以此二分。
如果通过先排序,再固定左端点二分右端点的方法计算,单次复杂度为\(O(nlogn)\),虽然可以通过此题,但是对于再大的\(n\),就束手无策了。
为此我们可以用Two Points,左指针从1,右指针从n,每次固定左端点向右端点找一个恰好满足\(a_l+a_r<mid\)的位置,将答案加上\(r-l\)再右移左指针,就可以在\(O(n)\)时间内完成单次查询。
代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define rep(i,x,y) for (int i=x;i<=y;i++)
#define dep(i,y,x) for (int i=y;i>=x;i--)
using namespace std;
typedef long long LL;
const int maxn=200000+32;
LL a[maxn],n,k,l,r,mid,ans;
LL get(LL p)
{
LL r=n,ans=0;
rep(l,1,n)
{
while ((l<r)&&(a[l]+a[r])>=p) r--; //右移找位置
if (l>=r) break;
ans+=r-l;
}
return ans;
}
int main()
{
scanf("%I64d%I64d",&n,&k);
rep(i,1,n) scanf("%I64d",&a[i]);
sort(a+1,a+n+1);
l=0,r=a[n]*2+1;
while (l<(r-1)) //二分答案
{
mid=(l+r)>>1;
if (get(mid)<k) l=mid; else r=mid-1;
}
if (get(r)<k) ans=r; else ans=l;
printf("%I64d\n",ans);
return 0;
}

浙公网安备 33010602011771号