卧薪尝胆,厚积薄发。
POI2008 BBB-BBB
Date: Wed Sep 26 09:39:17 CST 2018
In Category:
NoCategory
Description:
给定一个由
$+1$
和
$−1$
构成的长度为
$n$
的序列,提供两种操作:
$1.$
将某一位取反,花销为
$x$
。
$2.$
将最后一位移动到第一位,花销为
$y$
。
要求最终
$p+sum=q$
,且
$\forall i\in[1,n]p+sum_i\geqslant0$
,求最小花销。
$1\leqslant n \leqslant 10^6$
Solution:
循环移位非常不好处理,这题的做法是断环成链,把环复制两倍,也就是说暴力枚举移位,这样移位的代价是可以计算出的,剩下的代价还有两部分,保证序列所有位置为正和最终结果为
$q$
,对于第一部分,发现只要让当前前缀和最小的位置前缀和为正了,就一定有一种方案让整个前缀和序列为正,设最小的前缀和为
$k$
,这个可以单调队列求出,那么这部分的花销是
$\lceil\frac{-k} 2\rceil$
,因为改变一个位置的值会产生
$2$
的变化,当前序列的总和是
$sum[n]+p+\lceil\frac{-k}2\rceil\times 2$
,要想让它变成
$q$
,就要再付出他们的差值除二上取整乘
$x$
的花费,最后和答案取
$\min$
即可,复杂度
$O(n)$
。
Code:
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
using namespace std;
int n,p,q,x,y;
#define MAXN 2000010
inline char getc()
{
register char c = getchar();
while(c != '-' && c != '+')c = getchar();
return c;
}
int a[MAXN],sum[MAXN];
int main()
{
scanf("%d%d%d%d%d",&n,&p,&q,&x,&y);
for(int i = 1;i <= n;++i)
{
if(getc() == '-')a[i] = -1;
else a[i] = 1;
}
for(int i = n + 1;i <= 2 * n;++i)
{
a[i] = a[i - n];
}
sum[0] = p;
for(int i = 1;i <= 2 * n;++i)
{
sum[i] = sum[i - 1] + a[i];
}
deque<int> qu;
for(int i = 1;i < n;++i)
{
while(!qu.empty() && sum[i] <= sum[qu.back()])qu.pop_back();
qu.push_back(i);
}
int ans = 0x3f3f3f3f;
for(int i = n;i < 2 * n;++i)
{
while(!qu.empty() && sum[i] <= sum[qu.back()])qu.pop_back();
qu.push_back(i);
while(!qu.empty() && qu.front() <= i - n)qu.pop_front();
int val = sum[qu.front()] - sum[i - n] + p;
int res = 0;
res += (n - (i - n)) % n * y;
if(val < 0)res += (-val + 1) / 2 * x;
int cur = sum[n];
if(val < 0)cur += (-val + 1) / 2 * 2;
res += (abs(cur - q) + 1) / 2 * x;
ans = min(ans,res);
}
cout << ans << endl;
return 0;
}
Copyright © 2020
wjh15101051
ღゝ◡╹)ノ♡