【POI2008】bzoj1122 账本

来源:互联网 发布:心理学量表 知乎 编辑:程序博客网 时间:2024/06/11 20:56

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

修改消耗的时间

首先考虑没有翻转操作。这样的话如果+太多就尽量把后面的+改成-,如果-太多就尽量把前面的-改成+【称为第一类修改】。然后需要考虑的位置就是前缀和最小的位置,如果那里小于零,就需要再把前面的一些-改成+,后面的+改成-【称为第二类修改】,这样是一定可以改过来的,因为只要把之前所有出现过的-都改成+最终的前缀和一定为正。
因为翻转和修改是没有关系的,不妨设先翻转再修改。
把序列变成一个环,枚举起点,也就是枚举翻转的次数。因为不管序列如何排布,第一类修改的方法都是相同的,所以在考虑第一次修改之前求出的最小前缀和,就是上面所说第一类修改完成之后的最小前缀和【不是数值相等,而是位置相同,也就是知道前者可以方便地O(1)知道后者】。用单调队列维护一下,很容易O(n)求出每个起点的最小前缀和。然后枚举起点,按照前面所说进行计算,需要计算的只是第二类修改的次数,这是可以O(1)列式计算的。
第一类修改会让和变化2,这很容易看出。但是,第二类修改同样会让前缀和变化2,这是需要注意的。

#include<cstdio>#include<cstring>#define LL long longconst LL oo=1e16;int a[3000010],n,que[3000010];LL sum[3000010],f[3000010];char s[1000010];LL abs(LL x){    return x>0?x:-x;}LL min(LL x,LL y){    return x<y?x:y;}int main(){    int i,j,k,p,q,hd,tl;    LL num,temp,ans,u,v,x,y,z;    scanf("%d%d%d%lld%lld",&n,&p,&q,&x,&y);    scanf("%s",s+1);    for (i=1;i<=n;i++)      a[i]=s[i]=='+'?1:-1;    for (i=n+1;i<2*n;i++)      a[i]=a[i-n];    for (i=1;i<2*n;i++)      sum[i]=sum[i-1]+a[i];    hd=1;    tl=0;    for (i=2*n-1;i;i--)    {        if (hd<=tl&&que[hd]>i+n-1) hd++;        while (hd<=tl&&sum[que[tl]]>=sum[i]) tl--;        que[++tl]=i;        f[i]=sum[que[hd]]-sum[i-1];    }    num=(p+sum[n]-q)/2;    ans=oo;    for (i=1;i<=n;i++)    {        u=i==1?0:n+1-i;        temp=u*y+abs(num)*x;        v=num<0?p+f[i]-num*2:p+f[i];        if (v<0) temp+=2*x*((1-v)/2);        ans=min(ans,temp);    }    printf("%lld\n",ans);}
0 0
原创粉丝点击