C. Theofanis' Nightmare

链接:https://codeforces.com/problemset/problem/1903/C
洛谷链接(有翻译):https://www.luogu.com.cn/problem/CF1903C
解答:
观察可知:如果后面连续的和大于等于0,那么后面就可以连成一段(贪心),就是说因为前面每加上一个数,后面的所有数出现的次数都会+1,那么只要后面的和大于等于0,就可以知道一定是越来越大的。
然后,如果发现后缀和小于零的时候,就开始合并掉某段:截至大于0为止的某段,这样也是最小,然后利用后缀和算出来该区间的值,进入vector。最后求和就可以。
最搞人心态的就是不要把后缀和清零,就是说所有后缀和必须以到结尾为止,因为我们每在前面加上一个数,会给后面所有数的次数+1,那么判断的时候就必须要用到结尾的后缀和。

代码:

#include<iostream>
#include<vector>
#include<algorithm>
#include<math.h>
#include<sstream>
#include<string>
#include<string.h>
#include<iomanip>
#include<stdlib.h>
#include<map>
#include<queue>
#include<limits.h>
#include<climits>
#include<fstream>
#include<stack>
#include<set>
typedef long long ll;
using namespace std;
const int N = 1e5 + 10;
ll lst[N];
ll lstadd[N];
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int t; cin >> t;
	while (t--)
	{
		memset(lstadd, 0, sizeof(lstadd));
		memset(lst, 0, sizeof(lst));
		int n; cin >> n;
		vector<ll>ut;
		for (int i = 0; i < n; i++)cin >> lst[i];
		int bl = n - 1;
		int his = bl;
		ll ale = 0;
		lstadd[n - 1] = lst[n - 1];
		for (int i = n - 2; i >= 0; i--)
		{
			lstadd[i] = lst[i] + lstadd[i+1];
		}
		//后往前找到和为最长正
		int ik = n - 1;
		while (ik >= 0)
		{
			while (ik >= 0 and lstadd[ik] >= 0)
			{
				ut.push_back(lst[ik]);
				ik--;
			}
			if (ik >= 0 and lstadd[ik] < 0)
			{
				int his = ik;
				while (ik >= 0 and lstadd[ik] < 0)
					ik--;
				if (ik >= 0) { ut.push_back(lstadd[ik] - lstadd[his + 1]); ik--; }
				else ut.push_back(lstadd[0] - lstadd[his + 1]);
			}
		}
		ll ans = 0;
		for (ll i = 1; i <= ut.size(); i++)
			ans += ut[ut.size() - i] * i;
		cout << ans << '\n';
	}
	return 0;
}

debug好久.....可恶!

posted on 2024-05-01 09:48  WHUStar  阅读(8)  评论(0编辑  收藏  举报