题目来源:http://codeforces.com/gym/101234/problem/J
单调队列在线做法,复杂度为O(q*n)。
思路来自:https://blog.youkuaiyun.com/acterminate/article/details/79416416
设sum[i]表示字符串的前缀和,则区间[l,r]内0的个数即为sum[r]-sum[l-1]。
因此所要求的即是r-(l-1)-(sum[r]-sum[l-1])+x-(sum[r]-sum[l-1])对于所有l和r的最大值。
化简后得r-2*sum[r]-((l-1)-2*sum[l-1])+x。
因此,对于每个r,只需找出最小的((l-1)-2*sum[l-1])即可,可以用单调队列进行维护。
代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <list>
#define ll long long
#define ull unsigned long long
#define BUG printf("************\n")
using namespace std;
const int maxn = 1e6 + 10;
char c[maxn];
int n, sum[maxn], m;
int q[maxn * 5];
int main() {
/*ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);*/
c[0]='1';
scanf("%s", c + 1);
n = strlen(c + 1);
scanf("%d", &m);
int x;
for (int i = 1; i <= n; ++i)
sum[i] = sum[i - 1] + c[i] - '0';
while (m--) {
scanf("%d", &x);
int l = 1, r = 0;
int ans = 0;
q[++r] = 0;
for (int i = 1; i <= n; ++i) {
while (l <= r && q[r] - sum[q[r]] * 2 > i - sum[i] * 2)
--r;
q[++r] = i;
while (l <= r && sum[q[r]] - sum[q[l]] > x)
++l;
ans = max(ans, q[r] - 2 * sum[q[r]] - (q[l] - 2 * sum[q[l]]) + x);
}
printf("%d\n", min(ans, n - sum[n]));
}
return 0;
}