[atAGC045B]01 Unbalanced
将0变为-1后求前缀和,那么$s$的价值即为最大的前缀和-最小的前缀和(特别的,空前缀的前缀和为0)
令$f(x)$表示当最大的前缀和不大于$x$时,最小的前缀和最大是多少,答案即为$\min_{x}x-f(x)$
考虑如何求$f(x)$,可以贪心,先令所有位置都为-1,记最大的前缀和为$k$(要有$k\le x$),然后从前往后贪心将每一个-1变为1并判断是否仍然有$k\le 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 }