原题传送门
首先确定一个指导思想,就是先确定把多少个移到前面来,再计算取反哪些
所以枚举把多少个移到前面来,就是把整个数列弯成一个环,在环上枚举起点就好了
确定了起点之后,其实可以直接求出答案。
考虑要满足两个条件,任意前缀和 > = − p >=-p >=−p, 总 和 + p = q 总和+p=q 总和+p=q
显然第一个条件先要满足
先求出以这个点为起点,最小的前缀和,这个部分直接用单调队列搞定
对于这个最小前缀和 s u m sum sum,如果 s u m + p < 0 sum+p<0 sum+p<0,就要花代价解决
之后再考虑全局进行一些取反操作,这个也是可以直接算的,肯定存在一种合理的情况
Code:
#include <bits/stdc++.h>
#define maxn 3000010
using namespace std;
int a[maxn], sum[maxn], q[maxn], n, p, Q, x, y;
char s[maxn];
int main(){
scanf("%d%d%d%d%d", &n, &p, &Q, &x, &y);
scanf("%s", s + 1);
for (int i = 1; i <= n; ++i) a[i] = s[i] == '-' ? -1 : +1;
for (int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + a[i];
for (int i = n + 1; i <= n * 2; ++i) sum[i] = sum[i - 1] + a[i - n];
int h = 1, t = 0;
for (int i = n << 1; i > n; --i){
while (h <= t && sum[q[t]] >= sum[i]) --t;
q[++t] = i;
}
int ans = 1e9;
int tmp = sum[q[h]] - sum[n];
if (tmp + p >= 0) ans = min(ans, (abs(p + sum[n] - Q) + 1) / 2 * x);
else{
int z = ((abs(tmp + p) + 1) / 2);
ans = min(ans, z * x + (abs(p + sum[n] - Q + z * 2) + 1) / 2 * x);
}
for (int i = n; i; --i){
while (h <= t && q[h] >= i + n) ++h;
while (h <= t && sum[q[t]] >= sum[i]) --t;
q[++t] = i;
tmp = sum[q[h]] - sum[i - 1];
if (tmp + p >= 0) ans = min(ans, (abs(p + sum[n] - Q) + 1) / 2 * x + (n + 1 - i) * y);
else{
int z = ((abs(tmp + p) + 1) / 2);
ans = min(ans, z * x + (abs(p + sum[n] - Q + z * 2) + 1) / 2 * x + (n + 1 - i) * y);
}
}
printf("%lld\n", ans);
return 0;
}