AGC045B 01 Unbalanced
有个01序列,其中有些位置不确定。你要确定这些位置,最小化最大的区间0和1出现次数的差的绝对值。
\(n\le 10^6\)
把01分别看成\(\pm 1\)。显然可以前缀和之后转化成最大值减最小值之差。
设\(F(M)\)表示最大值不超过\(M\)时,最小值最大是多少。求出\(Z\)表示最小的可能的最大前缀和,把所有不确定位置都取\(-1\)可得。可以贪心求出\(F(M)\):先一开把所有的不确定位置变成\(-1\),从前往后扫,如果当前位置调成\(1\)后面的最大值依然不超过\(M\),那么就调成\(1\)。最后\(\min_{M\ge Z} M-F(M)\)就是答案。这是\(O(n^2)\)的做法。
可以优化:考虑\(F(M)\)到\(F(M+2)\)的变化。在做\(F(M)\)的过程中,第一次遇到把不确定位置调成\(1\)就会超过\(M\)的位置,在这里做\(F(M+2)\)的过程中能够调成\(1\),然后因为已经给后缀全体加\(2\)所以剩下的和做\(F(M)\)时一样。如果最小值在这个位置后面,就顶多会加\(2\)。因此有\(F(M)+2\ge F(M+2)\),也就是\(F(M)-M\ge F(M+2)-(M+2)\)。
于是只需要算\(F(Z)\),\(F(Z+1)\)。时间\(O(n)\)。
using namespace std;
#include <bits/stdc++.h>
#define N 1000005
char str[N];
int n,a[N],s[N];
int suc[N];
int calc(int lim){
int c=0,res=0;
for (int i=1;i<=n;++i){
if (a[i]==0){
if (suc[i]+c+2<=lim)
c+=2;
}
res=min(res,s[i]+c);
}
return res;
}
int main(){
freopen("in.txt","r",stdin);
scanf("%s",str+1);
n=strlen(str+1);
for (int i=1;i<=n;++i)
a[i]=(str[i]=='0'?1:str[i]=='1'?-1:0);
int mx=0;
for (int i=1;i<=n;++i)
s[i]=s[i-1]+(a[i]<=0?-1:1),mx=max(mx,s[i]);
suc[n]=s[n];
for (int i=n-1;i>=1;--i)
suc[i]=max(suc[i+1],s[i]);
int ans=min(mx-calc(mx),mx+1-calc(mx+1));
printf("%d\n",ans);
return 0;
}