[atAGC045B]01 Unbalanced

将0变为-1后求前缀和,那么$s$的价值即为最大的前缀和-最小的前缀和(特别的,空前缀的前缀和为0)

令$f(x)$表示当最大的前缀和不大于$x$时,最小的前缀和最大是多少,答案即为$min_{x}x-f(x)$

考虑如何求$f(x)$,可以贪心,先令所有位置都为-1,记最大的前缀和为$k$(要有$kle x$),然后从前往后贪心将每一个-1变为1并判断是否仍然有$kle x$,通过维护后缀最大值可以做到$o(n)$

关于贪心正确性的证明:考虑构造,将非最优解第一个与最优解不同的位置改成最优解的位置

(这里的位置指修改的位置,即-1变成1的位置所构成的集合)

较优解相当于对这两个位置之间全部加2,那么$f(x)$必然更大,同时对于这段区间之前的操作较优解和最优解相同(但最优解在这段区间中可能还有修改),最优解合法,较优解一定也合法

(最优解指通过贪心所得到的解,较优解指由非最优解构造得到的解)

进一步的,考虑$x$和$x-2$,若后者仍有位置填的是-1,那么前者一定是通过后者将第一个-1的位置改成1得到(联系贪心的过程,这里可以改成1,且之后与$x-2$相同)

对于$f(x)$和$f(x-2)$,前者即为将后者的一个后缀增加2,即有$f(x)le f(x-2)+2$,简单的变形可以得到$(x-2)-f(x-2)le x-f(x)$

设$k$为所有位置都填-1时的最大前缀和,即仅需要计算$f(k)$和$f(k+1)$,复杂度降为$o(n)$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 1000005
 4 int n,sum[N],mx[N];
 5 char s[N];
 6 int calc(int k){
 7     int tag=0,ans=0;
 8     for(int i=0;i<n;i++){
 9         if ((s[i]=='?')&&(2*(tag+1)+mx[i+1]<=k))tag++;
10         ans=min(ans,2*tag+sum[i+1]);
11     }
12     return ans;
13 }
14 int main(){
15     scanf("%s",s);
16     n=strlen(s);
17     for(int i=0;i<n;i++)
18         if (s[i]=='1')sum[i+1]=sum[i]+1;
19         else sum[i+1]=sum[i]-1;
20     mx[n]=sum[n];
21     for(int i=n;i;i--)mx[i-1]=max(sum[i-1],mx[i]);
22     printf("%d",min(mx[0]-calc(mx[0]),mx[0]+1-calc(mx[0]+1)));
23 } 
View Code
原文地址:https://www.cnblogs.com/PYWBKTDA/p/14168277.html