[bzoj1122][贪心][乱搞]账本BBB

55 篇文章 0 订阅
22 篇文章 0 订阅

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值