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;
}
posted @ 2016-09-26 19:41  Krew  阅读(238)  评论(0)    收藏  举报