免费的黄冈网站有哪些代码/河南网站推广公司
目录
Description
Format
Input
Output
Samples
输入数据 1
输出数据 1
输入数据 2
输出数据 2
Limitation
分析
代码
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个初始元素*/elseif(f[i]-f[j]<q.top())break;/*如果当前子段和已小于前k大的最小值,就停止枚举*/elseq.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]<<' ';/*因为是小根堆,所以要倒过来输出*/
}
注:
本人第一次发博客,如有不妥之处,望各位指正,谢谢!