金题大战Vol.0 A凉宫春日的叹息
Posted liuchanglc
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了金题大战Vol.0 A凉宫春日的叹息相关的知识,希望对你有一定的参考价值。
金题大战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\%)的数据,(nleq 10^5)
对于(100\%)的数据,(nleq 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
",l);
return 0;
}
以上是关于金题大战Vol.0 A凉宫春日的叹息的主要内容,如果未能解决你的问题,请参考以下文章
csps模拟9495凉宫春日的忧郁,漫无止境的八月,简单计算,格式化题解