1. 程式人生 > >[bzoj1122][貪心][亂搞]賬本BBB

[bzoj1122][貪心][亂搞]賬本BBB

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

的關係
這樣的話只需要讓 m i n ( p r e f
i x [ i ] ) min(prefix[i])
最小即可
顯然是把-1變成1,一次貢獻是2,只需要變 p r e f i x [ i ] / 2 \lceil -prefix[i]/2 \rceil
變完之後顯然 m i n ( p r e f i x [ i ] ) > = 0 min(prefix[i])>=0
再考慮 p + p r e f i x [ n ] p+prefix[n] q q 的關係
-1變1顯然在儘量靠前的位置
1變-1顯然在儘量靠後的位置
小於的時候是把-1變1對 m i n ( p r e f i x [ i ] ) min(prefix[i]) 沒有影響可以直接做了
大於的時候其實也沒有影響…這個想一想就可以知道的
所以知道 m i n ( p r e f i x [ i ] ) min(prefix[i]) 之後 變的次數是固定的
可以 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;
}