Tyvj P3572 BZOJ 1122 账本BBB

本文介绍了一种通过贪心算法和单调队列解决记账单修复问题的方法,旨在确保账户余额不会变为负数并最终达到指定金额。文章详细阐述了算法实现过程及注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

描述

一个长度为n的记账单,+表示存¥1,-表示取¥1。 
现在发现记账单有问题。 
一开始本来已经存了¥p,并且知道最后账户上还有¥q。 
你要把记账单修改正确,使得 
1:账户永远不会出现负数; 
2:最后账户上还有¥q。 
你有2种操作: 
1:对某一位取反,耗时x; 
2:把最后一位移到第一位,耗时y。 

输入格式

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)

输出格式

修改消耗的时间

测试样例1

输入

9 2 3 2 1 
---++++++

输出


3

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

单调队列+贪心~

因为有一个把最后一位移到第一位的操作,所以用类似于破环为链的方法,重复两次数组,计算前缀和。

维护单调队列,计算每个位置的最小值,记录到数组中,最后枚举移位次数,计算该移位次数下的最小值,更新ans值。

(数组大得好夸张,吓得我都不敢交了……)

(在BZOJ上交要把输出改成lld,否则会PE~)


#include<cstdio>  
#include<cstring>  
#include<iostream>  
using namespace std;  
#define ll long long  
  
int n,p,q,x,y,que[1000100<<1],head,tail,num[1000100<<1];  
ll tot,ans,cal[1000100],fi[1000100],now;  
char s[1000100];  
  
ll abs(ll u)  
{  
    return u>0 ? u:-u;  
}  
  
int main()  
{  
    scanf("%d%d%d%d%d",&n,&p,&q,&x,&y);  
    scanf("%s",s+1);head=1;tail=0;  
    for(int i=n*2;i>n;i--) num[i]=num[i+1]+(s[i-n]=='+' ? 1:-1);tot=num[n+1];  
    for(int i=n;i;i--) num[i]=num[i+1]+(s[i]=='+' ? 1:-1);  
    for(int i=n*2;i;i--)  
    {  
        while(head<=tail && num[i]>num[que[tail]]) tail--;  
        que[++tail]=i;  
        while(head<=tail && que[head]-i>n) head++;  
        if(i<=n) fi[i]=num[i]-num[que[head]];  
    }  
    ans=0x7f7f7f7f7f7f7f7fll;  
    cal[1]=1;  
    tot=q-p-tot;tot/=2;  
    for(int i=2;i<=n;i++) cal[i]=n-i+2ll;  
    for(int i=0;i<n;i++)  
    {  
        now=i*y+abs(tot)*x;  
        fi[cal[i+1]]+=p+max(tot,0ll)*2ll;  
        if(fi[cal[i+1]]<0) now+=((1-fi[cal[i+1]])/2ll)*2*x;  
        ans=min(ans,now);  
    }  
    printf("%I64d\n",ans);  
    return 0;  
}  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值