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好久.....可恶!