题目大意:有一个n个数构成的数组a,每次操作可以使其中一个数取反,给出一个整数m,定义为数组从1到i的前缀和,问至少要多少次操作才能使得所有i属于1到n的大于等于
1<=m<=n<=2e5;-1e9<=ai<=1e9
思路:我们先看m和m左边的部分:
....
所以从m开始向左累加的每一个和都应该是小于等于0的,那么我们从m开始向左遍历,如果当前遇到的数本身就是小于等于0的,直接累加即可,如果不是,我们先将其放入优先队列中,因为我们之后操作肯定是对最大的数进行操作取得的收益最大,然后我们加上这个数,判断如果当前的累加和已经大于0了,就要-2倍的优先队列堆顶的数,直到累加和小于等于0,特殊的是a1不需要任何处理。
对于m右边的数,我们同样用之前的方法可以推出从第m+1个数开始,向右累加的每一个和都要大于等于0,向右遍历的操作也有上面类似,不再赘述
#include<__msvc_all_public_headers.hpp>
//#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
ll a[N];
int main()
{
cin.tie(0);
ios::sync_with_stdio(false);
int t;
cin >> t;
while (t--)
{
int n, m;
cin >> n >> m;
int ans = 0;//统计操作次数
ll summ = 0;//统计累加和
priority_queue<ll>q;//大的数优先级大
for (int i = 1; i <= n; i++)
{
cin >> a[i];
if (i == m )
{
if (m == 1)//m=1不需要对左边操作
continue;
for (int j = m; j > 1; j--)
{//从am到a2遍历
if (a[j] <= 0)
{//当前数小于等于0直接加
summ += a[j];
continue;
}
q.push(a[j]);
summ += a[j];
while (summ > 0)
{//当前累加和大于0要一直-2倍堆顶元素
ans++;
summ -= q.top()*2;
q.pop();
}
}
}
}
if (m == n)
{//m=n不需要对右边操作
cout << ans << endl;
continue;
}
summ = 0;
priority_queue<ll, vector<ll>, greater<ll>>q2;//小的数高优先级
for (int i = m + 1; i <= n; i++)
{//从m+1到n遍历
if (a[i] >= 0)
{
summ += a[i];
continue;
}
q2.push(a[i]);
summ += a[i];
while (summ < 0)
{
ans++;
summ -= q2.top()*2;
q2.pop();
}
}
cout << ans << endl;
}
return 0;
}