2022icpc网络赛(1)A 01 Sequence

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 a1​a2​⋯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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nj745

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值