GDUT专题学习5-D - Least Prefix Sum

D - Least Prefix Sum

著名棋手、数学家波罗的海有一个数组a1​,a2​,…,an​,并且他可以执行以下操作若干次(可能0)次:

  • 选择一些索引i (1≤i≤n);
  • 多样地ai​乘−1,也就是设定ai​:=−ai​.

波罗的海人最喜欢的数字是m,他想a1​+a2​+⋯+am​是所有非空前缀总和的最小值。更正式地说,对于每一个k=1,2,…,n它应该持有a1​+a2​+⋯+ak​≥a1​+a2​+⋯+am​.

请注意,可能存在多个最小前缀和,并且只要求a1​+a2​+⋯+am​是其中之一。

帮助Baltic找到所需的最少操作数使得a1​+a2​+⋯+am​为所有前缀和中最小的一个。保证有效的操作序列总是存在的。

投入

每个测试包含多个测试用例。第一行包含测试用例的数量t (1≤t≤10000).测试用例的描述如下。

每个测试用例的第一行包含两个整数n和m (1≤m≤n≤2⋅10^5)—波罗的海的数组大小和他最喜欢的数字。

第二行包含n个整数a1,a1​,a2​,…,an​ (−109≤ai​≤109)。

保证的是n所有测试案例不超过2*10^5.

输出

对于每个测试用例,打印一个整数——所需操作的最小数量。

样本1

输入输出
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

 思路

题目大意是使得长度为m的前缀和最小需要操作的最少次数,那么只要保证在m之前的所有前缀和都小于等于0,在m之后的所有前缀和都大于等于0即可,由于要操作次数最小,所以我们利用优先队列完成,在m之前的用优先队列存正数,如果m之前的前缀和大于0,则优先将最大整数变为负数,注意从m-1处往前跑一遍。在m之后的则从m+1处跑到尾即可。

代码

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
#define LL long long
const int N=2e5+10;
LL a[N];
int main(){
	int t,n,m,ans=0;
	LL sum=0;
	cin>>t;
	while(t--){
	cin>>n>>m;
	priority_queue<LL,vector<LL>,less<LL> >q1;
	for (int i=1;i<=n;i++){
		cin>>a[i];
		if(i==m){         
			for(int j=m;j>1;j--){
				if(a[j]<=0){
				sum+=a[j];
				continue;
     			}
     			q1.push(a[j]);
     			sum+=a[j];
    			while(sum>0){
				ans++;
				sum-=q1.top()*2;
				q1.pop();
				}
			}
		}
	}
	sum=0;
	priority_queue<LL,vector<LL>,greater<LL> >q2;
	for(int i=m+1;i<=n;i++){
		if (a[i]>=0){
			sum+=a[i];
			continue;
   		}
		q2.push(a[i]);
   		sum+=a[i];
   		while(sum<0){
    		ans++;
    		sum-=q2.top()*2;
    		q2.pop();
   		}   
  	}
  	cout<<ans<<endl;
 	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值