C. Least Prefix Sum(可以后悔的拿取+multiset)

Problem - C - Codeforces

 

波罗的海,一个著名的棋手,同时也是一个数学家,他有一个数组a1,a2,...,an,他可以进行以下几次(可能是0次)操作。

选择某个索引i(1≤i≤n)。
将ai与-1相乘,也就是说,设置ai:=-ai。
Baltic最喜欢的数字是m,他希望a1+a2+⋯+am是所有非空前缀之和中最小的一个。更正式地说,对于每一个k=1,2,...,n来说,应该是这样的
a1+a2+⋯+ak≥a1+a2+⋯+am.
请注意,可能存在多个最小的前缀和,只要求a1+a2+⋯+am是其中之一。

帮助Baltic找到使a1+a2+⋯+am成为所有前缀和中最小的一个所需的操作数。可以证明一个有效的操作序列总是存在的。

输入
每个测试包含多个测试案例。第一行包含测试用例的数量t(1≤t≤10000)。测试用例的描述如下。

每个测试用例的第一行包含两个整数n和m(1≤m≤n≤2⋅105)--Baltic数组的大小和他喜欢的数字。

第二行包含n个整数a1,a2,...,an(-109≤ai≤109)--数组。

保证所有测试案例的n之和不超过2⋅105。

输出
对于每个测试用例,打印一个整数--所需操作的最小数量。

例子
InputCopy
6
4 3
-1 -2 -3 -4
4 3
1 2 3 4
1 1
1
5 5
-2 3 -5 1 -20
5 2
-2 3 -5 -5 -20
10 4
345875723 -48 384678321 -375635768 -35867853 -35863586 -358683842 -81725678 38576 -357865873
输出拷贝
1
1
0
0
3
4
注意
在第一个例子中,我们进行操作a4:=-a4。数组变成[-1,-2,-3,4],前缀和[a1, a1+a2, a1+a2+a3, a1+a2+a3+a4],等于[-1,-3,-6,-2]。因此,a1+a2+a3=-6是所有前缀之和中最小的一个。

在第二个例子中,我们进行操作a3:=-a3。数组变成[1,2,-3,4],前缀和等于[1,3,0,4]。

在第三和第四个例子中,a1+a2+⋯+am已经是最小的前缀和了--不需要再进行操作。

在第五个例子中,一个有效的操作序列是。

a3:=-a3。
a2:=-a2。
a5:=-a5。
数组变成[-2,-3,5,-5,20],其前缀和为[-2,-5,0,-5,15]。注意a1+a2=-5和a1+a2+a3+a4=-5都是最小的前缀和(这也是一个有效的解)。

题解:

对于前缀和在k之后的,我们先不用管前面如何,假设前面已经成立,要想让后面的前缀和都大于k

假设k后面数小于0肯定是不行的,所以要维护后面的数加的过程中永远大于等于0,但是可能会有一种情况

999 -999 -1 -1 -1 -1

按理说-999时不用变换,因为结果还是大于等于0,但是对于后面来说要变换两次

如果我们变换-999只要一次就行

所以我们把每次加的数存进multiset里,一旦小于0,就-=2*(*s.begin()),把开头元素删除即可

同理对于k前的元素同一个思想

#include<iostream>
#include<algorithm>
#include<string>
#include<queue>
#include<vector>
#include<map>
#include<cstring>
#include<cmath>
#include<stack>
#include<set>
using namespace std;
#define int long long
typedef pair<int,int> PII;
int get(vector<int>a)
{
	int res = 0;
	multiset<int> s;
	int cnt = 0;
	for(auto x:a)
	{
		res += x;
		s.insert(x);
		while(res < 0)
		{
			res -= 2*(*s.begin());
			cnt ++;
			s.erase(s.begin());
		}
	}
	return cnt;
}
void solve()
{
	int n,k;
	cin >> n >>k;
	vector<int> a(n);
	for(int i = 0;i < n;i++)
	{
		cin >> a[i];
	}
	vector<int> b,c;
	for(int i = k;i < n;i++)
	{
		b.push_back(a[i]);
	}
	for(int i = k - 1;i > 0;i--)
	{
		c.push_back(-a[i]);
	}
	cout << get(b) + get(c)<<"\n";
}
//1 -7 1 -7
signed main(){
//	ios::sync_with_stdio(false);
//	cin.tie(0);
//	cout.tie(0);
	int t = 1;
	cin >> t;
    while(t--)
	{

		solve();
	} 
}
//5 2
//3 12

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值