传送门
题意:
给你一个长度为n的数组,你可以进行任意次操作(也可能是0),可以使,
然后给你一个数m,问你要进行多少次操作,才能使长度为m的前缀和的值在所有的前缀和中最小。
思路:
因为要使长度为m的前缀和在所有的前缀和中最小:
那么就可以表述为
我们分两部分1-m-1和m+1-n
1-m-1
然后右边去减去左边,可以得到
那么我们可以发现,从m-1开始的后缀和要小于等于0,
那么可以从m-1往前面走,如果后缀和大于0,那么就需要进行一个数进行取反操作,因为要的是最小的操作,那么就应该取可以取的最大的数进行取反,那么这一步就可以用一个优先队列来操作。
同理在(m+1,n)的前缀和要大于等于0。如果前缀和小于0,那么就需要进行一个数进行取反操作,因为要的是最小的操作,那么就应该取可以取的最小的数进行取反。那么这一步也可以用一个优先队列来操作。
那么就是从两边m开始往两遍走一边去找需要更新的操作,然后答案加起来就可以了。
代码:
#include<cstdio>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <string>
#include <math.h>
#include<vector>
#include<queue>
#include<map>
#define sc_int(x) scanf("%d", &x)
#define sc_ll(x) scanf("%lld", &x)
#define pr_ll(x) printf("%lld", x)
#define pr_ll_n(x) printf("%lld\n", x)
#define pr_int_n(x) printf("%d\n", x)
#define ll long long
using namespace std;
const int N=1000000+100;
int n ,m,h;
ll s[N],cnt[N];
int main()
{
int t;
sc_int(t);
while(t--)
{
cin>>n>>m;
int sum=0;
for(int i =1;i<=n;i++) cin>>s[i];
if(n==1){
cout<<"0\n";
continue;
}
ll k=0;
priority_queue<ll , vector<ll> , less<ll> >q;
for(int i =m;i>=2;i--)
{
q.push(s[i]);
k+=s[i];
if(k>0)
{
ll a=q.top();
q.pop();
sum++;
k+=(-a)*2;
}
}
priority_queue<ll , vector<ll> , greater<ll> >p;
k=0;
for(int i =m+1;i<=n;i++)
{
p.push(s[i]);
k+=s[i];
if(k<0){
ll a=p.top();
p.pop();
sum++;
k+=(-a)*2;
}
}
cout<<sum<<endl;
}
return 0;
}