传送门
题意:
给你一个长度为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;
}


















