Description
一个长度为n的记账单,+表示存¥1,-表示取¥1。现在发现记账单有问题。一开始本来已经存了¥p,并且知道最后账户上还有¥q。你要把记账单修改正确,使得
1:账户永远不会出现负数; 2:最后账户上还有¥q。你有2种操作: 1:对某一位取反,耗时x; 2:把最后一位移到第一位,耗时y。
Input
The first line contains 5 integers n, p, q, x and y (1 n 1000000,
0 p;q 1000000, 1 x;y 1000), separated by single spaces and
denoting respectively: the number of transactions done by Byteasar,
initial and final account balance and the number of seconds needed to
perform a single turn (change of sign) and move of transaction to the
beginning. The second line contains a sequence of n signs (each a plus
or a minus), with no spaces in-between. 1 ≤ n ≤ 1000000, 0 ≤ p ,q ≤
1000000, 1 ≤x,y ≤ 1000)
Output
修改消耗的时间
Sample Input
9 2 3 2 1
—++++++
Sample Output
3
题解
挺不错的一道题
先考虑没有2操作的情况
暂时不考虑 p , q p,q p,q的关系
这样的话只需要让 m i n ( p r e f i x [ i ] ) min(prefix[i]) min(prefix[i])最小即可
显然是把-1变成1,一次贡献是2,只需要变 ? ? p r e f i x [ i ] / 2 ? \lceil -prefix[i]/2 \rceil ??prefix[i]/2?次
变完之后显然 m i n ( p r e f i x [ i ] ) > = 0 min(prefix[i])>=0 min(prefix[i])>=0
再考虑 p + p r e f i x [ n ] p+prefix[n] p+prefix[n]与 q q q的关系
-1变1显然在尽量靠前的位置
1变-1显然在尽量靠后的位置
小于的时候是把-1变1对 m i n ( p r e f i x [ i ] ) min(prefix[i]) min(prefix[i])没有影响可以直接做了
大于的时候其实也没有影响…这个想一想就可以知道的
所以知道 m i n ( p r e f i x [ i ] ) min(prefix[i]) min(prefix[i])之后 变的次数是固定的
可以 O ( 1 ) O(1) O(1)
2操作的话,枚举2操作次数,实际上只需要维护一个动态前缀和最小值
单调队列即可
线段树被卡飞了啊…
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define lc now<<1
#define rc now<<1|1
using namespace std;
inline int read()
{
int f=1,x=0;char ch=getchar();while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';ch=getchar();}return x*f;
}
int stack[20];
inline void write(int x)
{
if(!x){
putchar('0');return;}int top=0;while(x)stack[++top]=x%10,x/=10;while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){
write(x);printf(" ");}
inline void pr2(int x){
write(x);puts("");}
int mn[2];int n,S,T,c1,c2;
char ch[1000005];
int a[2000005];
int pre[2000005],li[2000005],head,tail;
int main()
{
// freopen("4.in","r",stdin);n=read();S=read();T=read();c1=read();c2=read();//更改 移位 scanf("%s",ch+1);int to=0;for(int i=1;i<=n;i++)a[i]=a[i+n]=(ch[i]=='-'?-1:1),to+=a[i];for(int i=1;i<=2*n;i++)pre[i]=pre[i-1]+a[i];int ggg=0;int ans;head=1;tail=0;for(int i=2*n;i>=n+1;i--){
while(head<=tail&&pre[li[tail]]>=pre[i])tail--;li[++tail]=i;
// modify(1,1,2*n,i,i,ggg+a[i]);
// ggg+=a[i];}S+=to;mn[1]=pre[li[head]]-pre[n];if(S-to+mn[1]>=0)ans=(abs(S-T)+1)/2*c1;else{
int cnt=S-to+mn[1];ans=-(cnt-1)/2*c1;ans+=(abs(S-((cnt-1)/2*2)-T)+1)/2*c1;}for(int i=n;i>1;i--){
int sum=(n-i+1)*c2;while(head<=tail&&li[head]>=i+n)head++;while(head<=tail&&pre[li[tail]]>=pre[i])tail--;li[++tail]=i;mn[1]=pre[li[head]]-pre[i-1];if(S-to+mn[1]>=0)sum+=(abs(S-T)+1)/2*c1;else{
int cnt=S-to+mn[1];sum+=-(cnt-1)/2*c1;sum+=(abs(S-((cnt-1)/2*2)-T)+1)/2*c1;}ans=min(ans,sum);}pr2(ans);return 0;
}