1122: [POI2008]账本BBB
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 517 Solved: 248
[ Submit][ Status][ Discuss]
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
Sample Output
暴力枚举操作2,然后对于每种情况算一下操作1需要多少次求个最小值
枚举操作2相当于就是把序列当成一个环,然后枚举起点
主要在如何求操作1要多少次
首先算一下到底需要多少个'+'和多少个'-',如果'-'少了就把最后面的若干个'+'改掉,
如果'+'少了就把最前面的如干个'-'改掉
之后再看会不会出现p+前缀和为负的情况,这里需要用优先队列先求出每个起点的最小前缀和
最小前缀和知道后,如果为负就需要将前面的'+'改成'-',后面的'-'改成'+'
OK,最后求出最小值
#include<stdio.h>
#include<queue>
#include<algorithm>
using namespace std;
#define LL long long
deque<int> Q;
char str[1000005];
LL a[2000005], b[2000005], sum[2000005];
int main(void)
{
LL n, p, q, x, y, i, ans, now;
scanf("%lld%lld%lld%lld%lld%s", &n, &p, &q, &x, &y, str+1);
for(i=1;i<=n;i++)
a[i] = str[i]=='+'?1:-1;
for(i=n+1;i<=n*2;i++)
a[i] = a[i-n];
for(i=n*2;i>=1;i--)
sum[i] = sum[i+1]+a[i];
n *= 2;
for(i=n;i>=1;i--)
{
if(i<=n/2)
b[i] = sum[i]-sum[Q.back()];
while(Q.empty()==0 && sum[Q.front()]<=sum[i])
Q.pop_front();
Q.push_front(i);
while(Q.back()>=i+n/2)
Q.pop_back();
}
ans = 214748364721474847ll;
n /= 2;
for(i=1;i<=n;i++)
{
now = y*((n-i+1)%n);
if(p+sum[n+1]>=q)
now += (p+sum[n+1]-q)/2*x;
else
{
now += (q-p-sum[n+1])/2*x;
b[i] += (q-p-sum[n+1]);
}
if(p+b[i]<0)
now -= (p+b[i]-1)/2*2*x;
ans = min(ans, now);
}
printf("%lld\n", ans);
return 0;
}
/*
15 0 1 2 3
+--++---++--+++
15 5 0 1 1000
----------+++++
*/