码迷,mamicode.com
首页 > 其他好文 > 详细

金题大战Vol.0 A、凉宫春日的叹息

时间:2020-08-15 22:34:12      阅读:75      评论:0      收藏:0      [点我收藏+]

标签:cpp   不可   printf   std   using   ace   freopen   前缀   names   

金题大战Vol.0 A、凉宫春日的叹息

题目描述

给定一个数组,将其所有子区间的和从小到大排序,求第 \(k\) 小的是多少。

输入格式

第一行两个数\(n\),$ k\(,表示数组的长度和\)k$;

第二行有 \(n\) 个数,第\(i\)个是\(a[i]\),表示给定的数组。

输出格式

仅一个数,表示答案。

样例

样例输入1

5 6
1 1 1 1 1

样例输出1

2

样例输入2

8 20
2 3 1 2 5 3 2 3

样例输出2

8

数据范围与提示

对于\(15\%\)的数据,\(n \leq 1000\)

对于\(30\%\)的数据,\(n \leq 5000\)

对于\(50\%\)的数据,\(n,k \leq 10^5\)

对于\(70\%\)的数据,\(n\leq 10^5\)

对于\(100\%\)的数据,\(n\leq 10^6,1 \leq a[i],k \leq 10^9\)

分析

首先,这一道\(k\)的范围很大,因此我们肯定不可以把前\(k\)小的都求出来

所以我们只能换一种思路

我们观察一下数据范围,发现 \(n\) 只有 \(10^6\),而时限是 \(2s\)

似乎 \(n log n\) 的算法就可以过

于是我们就尝试二分枚举一个数,判断它能不能作为第 \(k\) 小的值

然后又会发现因为前缀和是单调递增的,所以就可以用双指针搞一下

这样每一次判断的复杂度就降低到了 \(O(n)\)

代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e6+5;
typedef long long ll;
inline ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<‘0‘ || ch>‘9‘){
		if(ch==‘-‘) f=-1;
		ch=getchar();
	}
	while(ch>=‘0‘ && ch<=‘9‘){
		x=(x<<1LL)+(x<<3LL)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
ll n,k,a[maxn],q[maxn],sum[maxn];
bool jud(ll now){
	memset(q,0,sizeof(q));
	ll head=1,tail=0,ans=0;
	for(ll i=1;i<=n;i++){
		while(head<=tail && sum[i]-sum[q[head]-1]>now) head++;
		q[++tail]=i;
		if(sum[i]-sum[q[head]-1]<=now)ans+=(i-q[head]+1);
	}
	return ans>=k;
}
int main(){
	freopen("A.in","r",stdin);
	freopen("A.out","w",stdout);
	n=read(),k=read();
	ll mmin=0x3f3f3f3f3f3f3f3f;
	for(ll i=1;i<=n;i++){
		a[i]=read();
		mmin=min(mmin,a[i]);
		sum[i]=sum[i-1]+a[i]*1LL;
	}
	ll l=mmin,r=sum[n],mids;
	while(l<=r){
		mids=(l+r)/2;
		if(jud(mids)) r=mids-1;
		else l=mids+1;
	}
	printf("%lld\n",l);
	return 0;
}

金题大战Vol.0 A、凉宫春日的叹息

标签:cpp   不可   printf   std   using   ace   freopen   前缀   names   

原文地址:https://www.cnblogs.com/liuchanglc/p/13504197.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!