Gym 101064 D Black Hills golden jewels 二分



D. Black Hills golden jewels
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

In Rapid City are located the main producers of the Black Hills gold jewelry, a very popular product among tourists. The main characteristics of these jewels are the designs of leaves and grape clusters that are made with gold alloys in yellow, green and pink tones.

Planning a trip to Rapid City in 2017, Nay Suames decides that he wants to buy these famous jewels, but he doesn't have much money to spend. Searching for the lowest prices, he found a jewelry store that will have a sale in May, the same month he will travel to that city. Lucky guy!

The jewelry store intends to sell the jewels that were produced with small defects, in general, only perceivable by experts. The jewels are separated in N categories of defects. Even if the defect is imperceptible, the jewelry store can't sell the jewel at full price, so it gives a discount proportional to the defect.

Knowing the high demand for these jewels, the jewelry store created a system aiming to be fair and, at the same time, to prevent everybody from buying only the cheapest jewels. The clients have to make a queue and will be served one at a time. When their time comes, the client must choose two jewels from different categories. Furthermore, they can't choose the same combination of categories that was previously chosen by another client.

Nay asks for your help to find the minimum amount of money he needs, being the K-th client in the queue, so that it is guaranteed that he can buy two different jewels.

Input

The first line contains two integers, N and K, representing the number of categories and the position of Nay in the queue, respectively. The next line contains N integers a1, a2, ..., aN (0 ≤ ai ≤ 109), corresponding to the prices of each category.

Output

Print a single integer, the minimum amount of money to guarantee that Nay can buy the jewels.

Examples
input
4 3
2 4 5 2
output
6
input
6 9
1 2 3 4 5 6
output
7
Note

    题意:内容介绍了很多,其实跟题意没什么关系。关键的题意可以浓缩成,给定一个序列,从序列中任意取两个数形成一个和,两个数不可相同,要求求出第k小的组合。

    分析:数据量是10的五次方,任取两个记录下来的话是10的10次方级的,再拍个序的话,肯定既超控件又超时间。但10的五次方级别的是可以拍个序的,排完序后就可以确定拿取组合的上限即最大和次大元素的组合,最小组合即最小和次小元素的组合。确定了上限和下限,又因为所求的值是有单调性的,所以考虑用二分。

    在确定使用二分之后,此时需要验证一个值是否满足条件。即任意组合中不大于该组合的组合数是否不小于k。若不小于k的话,则不断向左侧区间逼近,求出最小的符合的值。在求取组合数目时,如果使用普通的二重循环的话,肯定又超时。此时从最小的元素开始,设x为验证元素,使用upper_bound函数求出第一个大于x-a[i]的元素的位置,其函数返回的值即是和该元素相加不大于x的个数,即是组合数。upper_bound函数本质上也是一个二分。在此基础上可以进行一定的优化,在循环过程中只要所记数值大于等于k就可结束循环。知道大于x的a[i]标记,缩小循环范围也可优化。本题手写二分代码:

//两次二分
#include<stdio.h>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=100005;
LL a[maxn];
LL n,k;
int count(LL  x)//判断两数结合不大于k  的最大值
{
	LL cnt=0,end=n;//end值需要初始化 
	for(int i=1; i<=n; i++)
		if(a[i]>x)
		{
			end=i;
			break;
		}
//	printf("end= %d",end);
	for(LL i=1; i<=end; i++)//找到一个数和它的结合不大于k 即找到  x-a[i]最后可以插入的位置
	{
		int l=i,r=end,mid;
		while(l<=r)
		{
			mid=(l+r)>>1;
			if(a[i]+a[mid]<=x)//求第一个大于它的元素 
				l=mid+1;
			else
				r=mid-1;
		}//l 指的是 最小的加上a[i]大于x的元素下标
		cnt+=l-i-1;//满足条件元素的个数
		if(cnt>=k)//过程中判断
			return 1;
		if(l-i-1==0)
			break;
	}
	return 0;
}
int main()
{
	while(~scanf("%lld%lld",&n,&k))
	{
		for(LL i=1; i<=n; i++)
			scanf("%lld",&a[i]);
		sort(a+1,a+1+n);
		LL l=a[1]+a[2],r=a[n]+a[n-1],mid,res;//确定上限和下限
		while(l<=r)
		{

			mid=(l+r)>>1;
			int temp=count(mid);
			if(temp==1)
				r=mid-1;
			else
				l=mid+1;
		}
		printf("%lld\n",l);
	}
}

    本题使用 upper_bound函数解法:

//两次二分
#include<stdio.h>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=100005;
LL a[maxn];
LL n,k;
int count(LL  x)//判断两数结合不大于k  的最大值
{
	LL cnt=0,end;
	for(int i=1; i<=n; i++)
		if(a[i]>x)
		{
			end=i;
			break;
		}
	for(LL i=1; i<end; i++)//找到一个数和它的结合不大于k 即找到  x-a[i]最后可以插入的位置
	{
	/*	int l=i,r=end,mid;
		while(l<=r)
		{
			mid=(l+r)>>1;
			if(a[i]+a[mid]<=x)
				l=mid+1;
			else
				r=mid-1;
		}*/
		//printf("%lld + %lld >%lld \n",a[l],a[i],x);
		//cnt+=l-i-1;
		LL num=upper_bound(a+1+i,a+n+1,x-a[i])-(a+1+i);//找到第一个大于 x-a[i]的数
		cnt+=num;
		if(cnt>=k)//过程中判断
			return 1;
		if(num==0)//由于单调性 再往下找也找不到了
			break;
	}
	return 0;
}
int main()
{
	while(~scanf("%lld%lld",&n,&k))
	{
		for(LL i=1; i<=n; i++)
			scanf("%lld",&a[i]);
		sort(a+1,a+1+n);
		LL l=a[1]+a[2],r=a[n]+a[n-1],mid,res;//确定上限和下限
		while(l<=r)
		{

			mid=(l+r)>>1;
			//printf("mid=%d\n",mid);
			int temp=count(mid);
			//	printf("cnt=%d\n",temp);
			//	if(temp==k)
			//		res=mid;
			if(temp==1)
				r=mid-1;
			else
				l=mid+1;
		}
		printf("%lld\n",l);
	}
}

    比赛的时候是有这思路,也用二分写了,还是水平太菜,错误百出。哎……

    特记下,以备后日回顾。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值