Given a binary cyclic sequence S of length n, whose elements are either 0 or 1, you can do the following operation any number of times.
- Operation: if the length of S is greater than or equal to 3, choose a position i (1≤i≤n) such that Si=1, remove the element on position i and both its adjacent elements (remove 3 elements in total), and do not change the relative order of the other elements. Note that elements in the first and last positions are considered adjacent.
A binary cyclic sequence S is called good if you can make S empty using the operation above.
And the beauty of a binary cyclic sequence S, f(S), is defined as the minimum number of modifications needed to make S good. In a single modification you can flip an arbitrary element in S, that is, 0 becomes 1 and 1 becomes 0.
Given are a binary string a of length n and q queries. For the i-th query you are given two integers li and ri, and you should answer f(ali..ri) (where we consider the substring ali..ri as a cyclic sequence).
输入格式:
The first line contains two integers n and q (3≤n≤106, 1≤q≤106) — the length of string a and the number of queries, respectively.
The second line contains the string a1a2⋯an, where each ai is either 0 or 1.
Each of the following q lines contains two integers li and ri (1≤li≤ri≤n, (ri−li+1)≡0mod3) describing the i-th query.
输出格式
Output q lines, where the i-th line contains a single integer — the answer for the i-th query.
输入样例:
7 7
1000011
1 3
2 4
3 5
4 6
5 7
1 6
2 7
输出样例:
0
1
1
0
0
1
1
题意:给定一个01串,每次可以选择一个为1的数将它和相邻的两个数删除,或者将一个0反转为1,将一个区间所有数删除求最小的反转操作。
分析:对于一串连续的1,显然最优操作为每次删除两端的点(:这样只需消耗两个1即可删除3个点,而从中间删会消耗3个1),假设连续1的数量为c,那么最多可以进行ceil(1.0*c/2)次删除,
考虑前缀和维护 f[i] 为1到i能操作的次数,由于此题可以成环(即左端的1可以和右端的1连接),而两端为都为0时,显然有操作次数为 f[r]-f[l-1],考虑维护每个点向左(右)最近的0位置,对于任一区间长度为len(显然3的倍数)所需删除操作次数为len/3,则答案应为len/3-(f[r-pre[r]]-f[l+la[i]])-ceil((la[l]+pre[r])/2)【即任一区间的操作数为:两端连续1能操作的次数+中间以0为端点操作的次数】.
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,q,l,r;//输入数据
string s;
int pre[N],la[N];//向左(右)最近0位置
int f[N];//前缀和记录操作次数
void cal(int l,int r)
{
int c1=f[r-pre[r]]-f[l+la[l]-1];//中间以两端为0的操作次数
c1+=ceil(1.0*(la[l]+pre[r])/2);//两端连续1能操作的次数
printf("%d\n",max(0,(r-l+1)/3-c1));//输出答案,可能越界与0取最大值
}
void solve()
{
scanf("%d %d",&n,&q);
cin>>s;
int cnt=0,s1=0;//cnt记录当前连续1的数量,s1为前面每段连续1操作次数的和
for(int i=0;i<n;i++)
{
if(s[i]=='1')cnt++;
else s1+=ceil(1.0*cnt/2),cnt=0;
pre[i+1]=cnt;//+1保证下标从一开始,维护向左最近0位置
f[i+1]=ceil(1.0*cnt/2)+s1;//每个点的操作次数为当前连续1的操作次数+s1
}
cnt=0;
for(int i=n-1;i>=0;i--)
{
if(s[i]=='1')cnt++;
else cnt=0;
la[i+1]=cnt;//同理,维护向右最近0位置
}
while(q--)
{
scanf("%d %d",&l,&r);
cal(l,r);
}
}
int main ()
{
solve();
return 0;
}