[bzoj1122]账本

简化问题:如果没有2操作,答案是多少
贪心:修改-一定修改最前面的,修改+一定修改最后面的,正确性显然
而通过1操作,要完成两步:1.让最终结果为q;2.让前缀和非负,通过贪心可以获得最小值
(具体来说,假设初始有nq个+,np个-,第一步操作后前缀最小值为k,那么答案为$(|p+nq-np-q|/2+\max(p-k,0))x$)
那么枚举2操作的次数,考虑此时的答案,即要快速维护之前的贪心过程
前半部分的代价没有改变,相当于要快速维护前缀最小值
这个东西显然可以用优先队列来维护,复杂度$o(n)$
(同时要注意可能第二步的操作可以用第一步来实现(表达不清淅))

 1 #include
 2 using namespace std;
 3 #define N 2000005
 4 int n,p,qq,x,y,l,r,s2,a[N],sum[N],q[N];
 5 long long ans,s1;
 6 char s[N];
 7 int main(){
 8     scanf("%d%d%d%d%d%s",&n,&p,&qq,&x,&y,s);
 9     for(int i=1;i<=n;i++)
10         if (s[i-1]=='+')a[i]=a[i+n]=1;
11         else a[i]=a[i+n]=-1;
12     ans=1LL*n*x;
13     for(int i=1;i<2*n;i++){
14         while ((l;
15         sum[i]=sum[i-1]+a[i];
16         while ((l1]]))r--;
17         q[r++]=i;
18         if (i>=n){
19             if (i==n)s1=0;
20             else s1=n*2-i;
21             s1*=y;
22             s2=(1-p-sum[q[l]]+sum[i-n])/2;
23             if (s2<=0)ans=min(ans,s1+abs(p-qq+sum[n])/2LL*x);
24             else ans=min(ans,s1+1LL*s2*x+abs(p-qq+sum[n]+s2*2)/2LL*x);
25         }
26     }
27     printf("%lld",ans);
28 }
View Code

 

你可能感兴趣的:([bzoj1122]账本)