目录
Description
给你一个长度为n的一个正整数数组。 于是这个数列有n(n+1)/2个子段 。现在求出了这n(n+1)/2个子段之和,并降序排序,请问前K个数是多少。
Format
Input
第一行包含两个整数 n 和 k。 接下来一行包含 n 个正整数,代表数组。
ai≤10^9
k≤n(n+1)/2,
n≤100000,k≤100000
Output
输出 k 个数,代表降序之后的前 k 个数,用空格隔开
Samples
输入数据 1
3 4
1 3 4
输出数据 1
8 7 4 4
输入数据 2
5 5
9 1 1 1 1
输出数据 2
13 12 11 10 9
Limitation
1s, 1024KiB for each test case.
分析
对于这道题,大家应该都能想到暴力算法:枚举开始、结束点,算子段和,降序排列,取前k个值。但看看数据,会超时。所以,我们要进行一些优化。
1.看到前k大,你会想到什么?堆,对吧?
2.求子段和,是不是该用前缀和呢?
3.看到枚举。我们取的是前k大的值,而我们暴力枚举是先是1到1,再是1到2,1到3,直到1到n,明显是从小到大。那我们就来个逆循环。
4.如果当前子段和已经小于第k大的值,就可以break了(因为我们越枚举越小)。
代码
#include<bits/stdc++.h>
using namespace std;
priority_queue<long long,vector<long long>,greater<long long> >q;
/*小根堆*/
long long n,k,f[100100];
int main()
{
cin>>n>>k;
for(int i=1;i<=n;i++)
cin>>f[i],f[i]+=f[i-1]/*前缀和*/;
for(int i=n;i>=1;i--)/*逆循环*/
for(int j=0;j<=i;j++)
if(q.size()<k)
q.push(f[i]-f[j]);/*先加入k个初始元素*/
else
if(f[i]-f[j]<q.top())
break;/*如果当前子段和已小于前k大的最小值,就停止枚举*/
else
q.pop(),q.push(f[i]-f[j]);/*否则加入当前子段和*/
for(int i=1;i<=k;i++)
f[i]=q.top(),q.pop();
for(int i=k;i>=1;i--)
cout<<f[i]<<' ';/*因为是小根堆,所以要倒过来输出*/
}
注:
本人第一次发博客,如有不妥之处,望各位指正,谢谢!
该博客讨论了一道编程题目,要求求解一个正整数数组的n(n+1)/2个子段的降序排序前k个和。博主分析了暴力解法的局限,并提出了使用堆和前缀和优化的解决方案,通过逆循环和堆优化避免超时。文章包含了题目描述、输入输出格式、样例、时间限制和代码实现。
2478






