赛后补题:Hello 2023 CF1779C. Least Prefix Sum

传送门:CF

题目描述:

题目Latex过多,此处省略
输入:
6
4 3
-1 -2 -3 -4
4 3
1 2 3 4
1 1
1
5 5
-2 3 -5 1 -20
5 2
-2 3 -5 -5 -20
10 4
345875723 -48 384678321 -375635768 -35867853 -35863586 -358683842 -81725678 38576 -357865873
输出:
1
1
0
0
3
4

说句实话,这场比赛的C题感觉也就洛谷提高-难度,但是…我TM当时为什么就想不到正解.可能打到12点脑子糊了??真是醉了,上分之路路漫漫啊

主要思路:

  1. 题中需要求出sum[m]最小(sum代表前缀和),那么我们列举一下小于m之前的所有前缀和
    a [ 1 ] + a [ 2 ] + a [ 3 ] + . . . + a [ m ] > a [ 1 ] + a [ 2 ] + a [ 3 ] + . . . + a [ k ] a[1]+a[2]+a[3]+...+a[m]>a[1]+a[2]+a[3]+...+a[k] a[1]+a[2]+a[3]+...+a[m]>a[1]+a[2]+a[3]+...+a[k] 其中 k < m 其中k<m 其中k<m
    化解一下,就得出了 a [ k + 1 ] + . . . + a [ m ] < 0 a[k+1]+...+a[m]<0 a[k+1]+...+a[m]<0,也就是从m开始到2倒着的前缀和要一直为负
  2. 同理大于m的所有前缀和我们列举一下就会发现需要从m+1开始到n的所有前缀和要一直为正.然而当时我就卡在这一步了,我当时想法是当当前的前缀和大于0(以小于m为例),我们当前数字的显然是正的,所以当前数字应该直接取负的,反之取正.但是这种贪心想法是错误的,因为可能当前前缀和是负的,但是后来的数字加上去是正的,并且当前数字取反是最优的.举一个当时赛时自己举的反例:
6 5
1 5 2 8 -9 1
ans=1,直接改8即可
  1. 然后当时脑子就转不过来了,不知道该怎么办,当时的想法就是我该怎么知道8是不是应该取负呢.现在想想为什么当时就转不过来呢,就差一点点了啊.好了,讲讲正解吧.正解就是当我们当前的前缀和大于0(以小于m为例,大于m同理),我们将之前所有数字中最大的(包括自己)取出来取一个负数,这样的话因为只是将一个数字取反,所以我们将所有数字中最大的取负,这样在保证我们的前缀和为负的同时还保证了我们现在的前缀和是最小的

接来下是具体的代码部分:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <string.h>
#include <stack>
#include <deque>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define root 1,n,1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define int long long
#define maxn 1000000
#define ll_maxn 0x3f3f3f3f3f3f3f3f
const double eps=1e-8;
int t;int m,n;int a[maxn];
signed main() {
	t=read();
	while(t--) {
		n=read();m=read();int sum=0;int ans=0;
		priority_queue<int>q1;
		for(int i=1;i<=n;i++) a[i]=read();
		for(int i=m;i>=2;i--) {
			q1.push(a[i]);sum+=a[i];
			if(sum>0) {
				int f=q1.top();q1.pop();
				sum-=2*f;
				ans++;
			}
		}
		priority_queue<int,vector<int>,greater<int> >q2;
		sum=0;
		for(int i=m+1;i<=n;i++) {
			q2.push(a[i]);sum+=a[i];
			if(sum<0) {
				int f=q2.top();q2.pop();
				sum-=2*f;
				ans++;
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值