题意:有一个长度为n的01序列,你可以移动k次,每次将一个数移到任意一个位置,求经过操作后区间连续最大的连续0的个数。
“移动”操作看似情况很复杂,不好讨论,但其实无非就两种情况:
一、移动的是1:显然最优的策略是将1移动到最边上(相当于“移走”),目的是将两段连续的0合并。
二、移动的是0:最优策略是将小堆中的0移动到大堆里,目的是增加大堆中0的个数。
这样一来,情况就简单多了,问题转化成了求“将一段连续区间中的0合并,然后剩下的操作次数用于把其他地方的0引进来”的最优解,即求$min(max\left\{\sum\limits_{i\leqslant j,cnt0(i,j)\leqslant k}(cnt0(i,j)+(k-cnt1(i,j)))\right\},cnt0(1,n))$,前缀和+单调队列搞一搞就行了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=1e6+10; 5 char s[N]; 6 int n,m,k,a[N],b[N],hd,tl; 7 struct P {int x,y;} q[N]; 8 int main() { 9 scanf("%s",s+1),n=strlen(s+1); 10 for(int i=1; i<=n; ++i)a[i]=a[i-1]+(s[i]=='0'),b[i]=b[i-1]+(s[i]=='1'); 11 scanf("%d",&m); 12 while(m--) { 13 int ans=0; 14 scanf("%d",&k); 15 hd=tl=0; 16 for(int j=0,i=0; j<=n; ++j) { 17 for(; i<=j&&b[j]-b[i-1]>k; ++i); 18 for(; hd<tl&&q[hd].x<i-1; ++hd); 19 P np= {j,a[j]-b[j]}; 20 for(; hd<tl&&q[tl-1].y>=np.y; --tl); 21 q[tl++]=np; 22 ans=max(ans,(a[j]-b[j])+(k-q[hd].y)); 23 } 24 ans=min(ans,a[n]); 25 printf("%d\n",ans); 26 } 27 return 0; 28 }