题目:
![](https://i-blog.csdnimg.cn/blog_migrate/aef76bcac1c2fc63733f6e721871b51e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/e85531db762ed6aedfde9f2b19ab23ca.png)
![](https://i-blog.csdnimg.cn/blog_migrate/631a94d5f46cd794a7273ed3d115d096.png)
![](https://i-blog.csdnimg.cn/blog_migrate/0cfa42140591fbfe78a001dc11d7dbe8.png)
题意:对于数组n,可以进行的操作是对某一个位置的数字取反,要使得任意位置的前缀和都大于m位置的前缀和,最少需要进行多少次操作。
思路:
假设x是我们当前所在的位置,前缀和数组为s
当x=m时,显然有s(x)=s(m)
当x<m的时候,要使得s(x)>=s(m) ,说明在x~m上存在区间和小于0;所以对于1~m的部分从后往前累加,当累积小于0的时候,说明x的前面的部分是不用发生改动的;若累积大于0,则挑选区间里面最大的值来取反,再向前推进。
当x>m的时候,与上面同理,要保证m+1~x的部分的区间和是大于0的,所以累加到区间和小于0的情况,则把区间中最小的取反。
对于挑选区间最大和最小值采用优先队列来存储。
代码:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <queue>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
const ll MAXN=6e5;
const int mod=6e5;
const int N=1e6+10;
int t,n,m;
ll s[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>t;
while(t--){
//升序
priority_queue<ll,vector<ll>,greater<ll>>b;
priority_queue<ll>a;//降序
cin>>n>>m;
ll ans=0;
for(int i=1;i<=n;i++){
cin>>s[i];
}
ll count=0;
for(int i=m;i>1;i--){
count+=s[i];
a.push(s[i]);
if(count>0){
count-=a.top()*2;
a.pop();
ans++;
}
}
count=0;
for(int i=m+1;i<=n;i++){
count+=s[i];
b.push(s[i]);
if(count<0){
count-=b.top()*2;
b.pop();
ans++;
}
}
cout<<ans<<endl;
}
return 0;
}