【题解】LuoGu3470:[POI2008]BBB-BBB

原题传送门
首先确定一个指导思想,就是先确定把多少个移到前面来,再计算取反哪些
所以枚举把多少个移到前面来,就是把整个数列弯成一个环,在环上枚举起点就好了
确定了起点之后,其实可以直接求出答案。
考虑要满足两个条件,任意前缀和 > = − 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;
}

猜你喜欢

转载自blog.csdn.net/ModestCoder_/article/details/108561390
bbb
今日推荐