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;
}