题目描述

Peter喜欢玩数组。NOIP这天,他从Jason手里得到了一个大小为\(n\)的数组。

Peter求出了这个数组的所有子段和,并将这\(\frac{n(n+1)}{2}\)个数降序排列,他想知道前\(k\)个数是什么。

输入输出格式

输入格式

输入数据的第一行包含两个整数\(n\)和\(k\)。

接下来一行包含\(n\)个整数,代表数组。

输出格式

输出\(k\)个数,代表降序之后的前\(k\)个数,用空格隔开。

数据范围

题解

这个题目说的是十分的简洁明了,要求我们求出所有的子段和中前\(k\)大,首先看到这道题的时候,我用的二分答案加树状数组维护虽然在这道题上这种方法会T飞,但是,这种方法是一种方法是一种十分有效的算法。我们先二分答案来枚举第\(k\)大的子段和, 然后再用树状数组来维护和查询(就有点像求逆序对)。

具体过程:

我们每次枚举时,出第\(k\)大子段和为\(x\),那么\(x\)一定可以被表示为\(x = sum[i] - sum[j - 1]\)(\(sum[i]\)表示前缀和),我们把这个式子移项,可以得到\(sum[j - 1] = sum[i] - x\),这个式子表示当我们遍历到第 \(i\)个前缀和时,已知第\(k\)大的子段和为\(x\)那么我们只用找到\(sum[j - 1]\)之前有多少个\(sum[]\)就可以知道有多少个子段和比\(x\)大了。

#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) ((x) & (-x))
const int MAX = 100005;
int n, k;
long long a[MAX], sum[MAX], tree[MAX];
vector <long long> s; void Add(int x, long long val)
{
for(; x <= n; x += lowbit(x)) tree[x] += val;
} long long Query(int x)
{
long long ret = 0;
for(;x ; x-= lowbit(x)) ret += tree[x];
return ret;
} int check(int mid)
{
int ret = 0;
memset(tree, 0, sizeof(tree));
for(int i = 1; i <= n; ++ i)
{
int x = sum[i] - mid;
int it = lower_bound(s.begin(), s.end(), x) - s.begin();
ret += Query(it);
if(x > 0) ret ++;
it = lower_bound(s.begin(), s.end(), sum[i]) - s.begin();
Add(it + 1, 1);
}
return ret;
} int main()
{
// freopen("ksum.in", "r", stdin);
// freopen("ksum.out", "w", stdout);
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; ++ i)
{
scanf("%d", &a[i]);
sum[i] = sum[i - 1] + a[i];
s.push_back(sum[i]);
}
sort(s.begin(), s.end());
for(int t = 1; t <= k; ++ t)
{
long long l = 0, r = sum[n], mid, ans = 0;
for(;l < r;)
{
mid = (l + r) >> 1;
if(check(mid) >= t) l = mid + 1;
else r = mid;
}
printf("%lld ", l);
}
return 0;
}

如果求取的次数比较少的话,这也会是一个优秀的算法,但是,这道题的\(k\)太大,导致要多次重复这个过程所以,我们要考虑其他解法,因为数组中的数都是非负数,所以,我们可以来贪心。

  1. 最大的一定是所有数之和。
  2. 每次将最大的去头或去尾可以构成备选答案。

所以我们可以用优先队列来维护,但是,对于\([x,y]\)来说,它可能在\([x - 1, y]\)去头时加入,也有可能在\([x,y + 1]\)去尾时加入,这样就会重复,所以,我们需要一种不会重复的枚举方式,我们把所有前缀和入队,然后每次只考虑去头而不考虑去尾(在前缀和入队时已经去过了),这样就不会重复了。

#include<bits/stdc++.h>
using namespace std; struct Node{
int l, r;
long long sum;
bool friend operator < (const Node & x, const Node & y)
{
return x.sum < y.sum;
}
}; priority_queue <Node> q;
long long a[100005], sum = 0; Node make_Node(int l, int r, long long sum)
{
Node x;
x.l = l, x.r = r, x.sum = sum;
return x;
} int main()
{
int n, k;
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; ++ i)
{
scanf("%d", &a[i]);
sum += a[i];
q.push(make_Node(1, i, sum));
}
for(int i = 1; i <= k; ++ i)
{
Node x;
x = q.top(); q.pop();
printf("%lld ", x.sum);
q.push(make_Node(x.l + 1, x.r, x.sum - a[x.l]));
}
printf("\n");
return 0;
}

【题解】前k大子段和的更多相关文章

  1. HDU 6041 I Curse Myself(点双联通加集合合并求前K大) 2017多校第一场

    题意: 给出一个仙人掌图,然后求他的前K小生成树. 思路: 先给出官方题解 由于图是一个仙人掌,所以显然对于图上的每一个环都需要从环上取出一条边删掉.所以问题就变为有 M 个集合,每个集合里面都有一堆 ...

  2. 前k大金币(动态规划,递推)

    /* ///题解写的很认真,如果您觉得还行的话可以顶一下或者评论一下吗? 思路: 这题复杂在要取前k大的结果,如果只是取最大情况下的金币和,直接 动态规划递归就可以,可是前k大并不能找出什么公式,所以 ...

  3. 寻找无序数组中的前k大元素

    题目描述 以尽可能小的代价返回某无序系列中的两个最大值,当有重复的时设置某种机制进行选择. 题解 首先要考虑的是重复的数的问题. A.不处理重复数据方法:在处理第k大的元素时不处理重复的数据,也就是将 ...

  4. 7617:输出前k大的数

    7617:输出前k大的数 查看 提交 统计 提问 总时间限制: 10000ms 单个测试点时间限制: 1000ms 内存限制: 65536kB 描述 给定一个数组,统计前k大的数并且把这k个数从大到小 ...

  5. 输出前 k 大的数

    总时间限制: 10000ms 单个测试点时间限制: 1000ms 内存限制: 65536kB 描述 给定一个数组,统计前k大的数并且把这k个数从大到小输出. 输入 第一行包含一个整数n,表示数组的大小 ...

  6. 牛客第六场 J.Heritage of skywalkert(On求前k大)

    题目传送门:https://www.nowcoder.com/acm/contest/144/J 题意:给一个function,构造n个数,求出其中任意两个的lcm的最大值. 分析:要求最大的lcm, ...

  7. (算法)前K大的和

    题目: 1.有两个数组A和B,每个数组有k个数,从两个数组中各取一个数加起来可以组成k*k个和,求这些和中的前k大. 2.有N个数组,每个数组有k个数,从N个数组中各取一个数加起来可以组成k^N个和, ...

  8. 快速排序算法的实现 && 随机生成区间里的数 && O(n)找第k小 && O(nlogk)找前k大

    思路:固定一个数,把这个数放到合法的位置,然后左边的数都是比它小,右边的数都是比它大 固定权值选的是第一个数,或者一个随机数 因为固定的是左端点,所以一开始需要在右端点开始,找一个小于权值的数,从左端 ...

  9. 输出前k大的数

    总时间限制: 10000ms单个测试点时间限制:1000ms内存限制:65536kB(noi) 描述 给定一个数组,统计前k大的数并且把这k个数从大到小输出. 输入 第一行包含一个整数n,表示数组的大 ...

随机推荐

  1. 如鹏网学习笔记(十)DOM

    DOM笔记一.DOM简介 Document Object Model 文档对象模型 DOM的节点树模型:整个文档按照从大到小的节点划分,每一个内容都算作一个节点 DOM API 编程接口 可以用来操作 ...

  2. ADO MFC SQL2000

    对于初学VC的朋友来说,连接数据库其实是一件不容易的事情.记得我当时为了与数据库连接上,找了好多资料,上网看了好多文章,都没有解决这个问题.后 来,有个网友帮我解决了,我再次表示感谢.为了后来VC初学 ...

  3. [javaSE] 数据结构(二叉树-遍历与查找)

    前序遍历:中,左,右 中序遍历:左,中,右 后序遍历:左,右,中 二叉树查找 从根节点进行比较,目标比根节点小,指针移动到左边 从根节点进行比较,目标比根节点大,指针移动到右边 /** * 前序遍历 ...

  4. 关于对javaUtils封装和三层架构的笔记

    1.什么是三层架构: 三层架构(3-tier architecture) 通常意义上的三层架构就是将整个业务应用划分为:界面层(User Interface layer).业务逻辑层(Business ...

  5. 撩课-Web大前端每天5道面试题-Day9

    1. 请用至少3中方式实现数组去重? 方法一: indexOf ,,,,,,,,]; function repeat1(arr){ ,arr2=[];i<arr.length;i++){ ){ ...

  6. 配置JDK1.7开发环境

    学习java知识,首先要安装jdk来配置开发环境和java运行环境,本文介绍一下JDK配置流程和验证配置成功的方法. 一.配置JDK 1.首先下载jdk1.7压缩包并解压到D盘. 2.我的电脑--右键 ...

  7. sc create SVN-Service binpath= "D:\Program Files\Svn\bin\s vnserve.exe --service -r E:\repository\svn" displayname= "SVN-Service" start= au to depend= Tcpip [SC] OpenSCManager 失败 5:

    在安装SVN服务时就会出现如下问题: C:\Users\gushangzao>sc create SVN-Service binpath= "D:\Program Files\Svn\ ...

  8. C# 调用C/C++动态链接库,结构体中的char*类型

    用C#掉用C++的dll直接import就可以之前有不同的类型对应,当要传递结构体的时候就有点麻烦了,这里有一个结构体里边有char*类型,这个类型在C#中调用没法声明,传string是不行的默认st ...

  9. C# 调用C++DLL 类型转换

    内容转自网上····这里做 备份··· 原文链接: http://blog.csdn.net/miss_easy/article/details/52470964 /C++中的DLL函数原型为 //e ...

  10. 初学Node.js

    下载Node.js,官方网址:https://nodejs.org/en/download/ 可根据根据自己的电脑配置来下载相当于的Node.js 下载完成后使用Windows键+R 输入cmd 输入 ...