题意:有t组样例,每组样例有一组长度为n的数组,每操作一次可以使一个数变为它的相反数,问最少操作几次可以使任意i属于【1,n】,使si>=sm。
思路:该题主要展示了优先队列的基本功能和好处,对于该题可以分为三种情况,一种是i<m,此时si和sm中的一部分消掉以后,剩下的数的和保证<=0;第二种是i=m,此时两边都消掉,0=0,一定成立;第三种是i>m,此时si中的sm消掉以后,剩下的数>=0,而对于第一种情况,如果当前总和大于0,则一定有正数使其>0,而优先队列可以从大到小和从小到大排列队列中的所有元素,所以优先队列的头部一定是最大或最小值,基于此,每次取当前最大值或最小值,可以使总操作次数达到最小。需要注意的是,第一种情况需要倒着遍历,因为后面的元素不会影响前面的前缀和。
代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int i,j;
int t,n,m;
scanf("%d",&t);
while(t--){
priority_queue<int,vector<int>,greater<int> >b;
priority_queue<int,vector<int>,less<int> >a;
int s[200005]={0};
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++){
scanf("%d",&s[i]);
}
long long sum=0;
int ans=0;
for(i=m;i>1;i--){
sum+=s[i];
a.push(s[i]);
if(sum>0){
sum-=a.top()*2;
a.pop();
ans++;
}
}
sum=0;
for(i=m+1;i<=n;i++){
sum+=s[i];
b.push(s[i]);
if(sum<0){
sum-=b.top()*2;
b.pop();
ans++;
}
}
printf("%d\n",ans);
}
return 0;
}