序列操作

题目描述

lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作:

0 a b 把[a, b]区间内的所有数全变成0

1 a b 把[a, b]区间内的所有数全变成1

2 a b 把[a,b]区间内的所有数全部取反,也就是说把所有的0变成1,把所有的1变成0

3 a b 询问[a, b]区间内总共有多少个1

4 a b 询问[a, b]区间内最多有多少个连续的1

对于每一种询问操作,lxhgww都需要给出回答,聪明的程序员们,你们能帮助他吗?

输入格式

输入数据第一行包括2个数,n和m,分别表示序列的长度和操作数目

第二行包括n个数,表示序列的初始状态

接下来m行,每行3个数,op, a, b,(0<=op<=4,0<=a<=b<n)表示对于区间[a, b]执行标号为op的操作

输出格式

对于每一个询问操作,输出一行,包括1个数,表示其对应的答案

输入输出样例

输入

10 10
0 0 0 1 1 0 1 0 1 1
1 0 2
3 0 5
2 2 2
4 0 4
0 3 6
2 3 7
4 2 8
1 0 5
0 5 6
3 3 9

输出

5
2
6
5

说明/提示

对于30%的数据,1<=n, m<=1000

对于100%的数据,1<=n, m<=100000

 


 

 

询问总共有多少个1 可以直接维护区间和sum来解决

询问最多有多少个连续的1 可以考虑维护 一个节点对应区间的 maxl 与 maxr;分别代表左边开始最多的连续1的数目和右边开始最多的连续的1的数目

因为还有把0和1交换的操作

所以同时再维护一下一个区间的0的个数和sum0,以及连续的0的数目

tag的话 一个tag表示置0、置1或当前没有置01的操作 另一个tag表示翻转区间的0和1 遇到翻转时,可以直接修改置01的tag,遇到置01时,直接删除掉翻转tag,这里的更新操作是什么可以思考一下


 

 


附上dalao代码 https://www.luogu.org/blog/tanrui-2960967961/solution-p2572

#include <bits/stdc++.h>
//#define Int register int
#define MAXN 100005
using namespace std;

int n,m,a[MAXN];

struct node{ 
    int l,r,sum1,max1,max0,maxl0,maxr0,maxl1,maxr1,lazy1;
    bool overturn;
    int size (){
        return r - l + 1;
    }
}tree[MAXN << 2];

int Max (int a,int b,int c){
    if (a >= b && a >= c) return a;
    else if (b >= a && b >= c) return b;
    else if (c >= a && c >= b) return c;
}
void Push_up (int i){
    tree[i].sum1 = tree[i << 1].sum1 + tree[i << 1 | 1].sum1;
    tree[i].max1 = Max (tree[i << 1].max1,tree[i << 1 | 1].max1,tree[i << 1].maxr1 + tree[i << 1 | 1].maxl1);
    tree[i].max0 = Max (tree[i << 1].max0,tree[i << 1 | 1].max0,tree[i << 1].maxr0 + tree[i << 1 | 1].maxl0);
    //下面这些是看子区间长度够不够长来更新 
    tree[i].maxl0 = (tree[i << 1].maxl0 == tree[i << 1].size() ? tree[i << 1].size() + tree[i << 1 | 1].maxl0 : tree[i << 1].maxl0);
    //如果左儿子全是0,那该点最长连续的0长度为左儿子长加右儿子以左端点开始的连续的0长度 
    tree[i].maxl1 = (tree[i << 1].maxl1 == tree[i << 1].size() ? tree[i << 1].size() + tree[i << 1 | 1].maxl1 : tree[i << 1].maxl1); 
    //1同理   
    tree[i].maxr0 = (tree[i << 1 | 1].maxr0 == tree[i << 1 | 1].size() ? tree[i << 1 | 1].size() + tree[i << 1].maxr0 : tree[i << 1 | 1].maxr0);
    tree[i].maxr1 = (tree[i << 1 | 1].maxr1 == tree[i << 1 | 1].size() ? tree[i << 1 | 1].size() + tree[i << 1].maxr1 : tree[i << 1 | 1].maxr1);
    //反过来从右儿子右端点的情况也一样 
}
void Maintain (int i){
    tree[i].overturn = 0;//如果你都区间赋值了,就不用管翻转了    //???? 
    if (tree[i].lazy1 == 1){//区间赋值为零
        tree[i].max0 = tree[i].maxl0 = tree[i].maxr0 = tree[i].size();
        //连续的0最长长度变为树的长度 
        tree[i].max1 = tree[i].maxl1 = tree[i].maxr1 = tree[i].sum1 = 0;
        //连续的0最长长度变为0
    }
    else if (tree[i].lazy1 == 2){//区间赋值为1
        tree[i].max0 = tree[i].maxl0 = tree[i].maxr0 = 0;//与上面同理 
        tree[i].max1 = tree[i].maxl1 = tree[i].maxr1 = tree[i].sum1 = tree[i].size();
    }
}
void Mainflip (int i){//区间翻转
    tree[i].sum1 = tree[i].size() - tree[i].sum1;
    //该点的值变为长度减去原来的值(因为只有0和1,这样就相当于0全变成1,1全变成0) 
    swap (tree[i].max1,tree[i].max0);//最大连续0和最大连续1交换 
    swap (tree[i].maxl0,tree[i].maxl1);//左儿子的最大连续0和最大连续1交换
    swap (tree[i].maxr0,tree[i].maxr1);//右儿子的最大连续0和最大连续1交换 
}
void Push_down (int i){ //标记下传 
    if (tree[i].lazy1 == 1){//赋值为0
        tree[i << 1].lazy1 = tree[i << 1 | 1].lazy1 = 1;//将标记传给左右儿子 
        Maintain (i << 1);
        Maintain (i << 1 | 1);
        tree[i].lazy1 = 0;//赋完值清除标记 
    }
    else if (tree[i].lazy1 == 2){//赋值为1
        tree[i << 1].lazy1 = tree[i << 1 | 1].lazy1 = 2;
        Maintain (i << 1);
        Maintain (i << 1 | 1);
        tree[i].lazy1 = 0;
    }
    if (tree[i].overturn){//如果需要翻转
        tree[i << 1].overturn ^= 1;    //就是用1,不能用0 
        tree[i << 1 | 1].overturn ^= 1;//这里翻转懒标记取反就好了  
        Mainflip (i << 1);
        Mainflip (i << 1 | 1);
        tree[i].overturn = 0;
    }
}
void Build (int i,int l,int r){
    tree[i].l = l,tree[i].r = r;
    if(l == r){  //该节点只有一个值的话 
        tree[i].max1 = a[l];
        tree[i].max0 = !a[l];//0
        //cout<<!a[l]<<endl;
        tree[i].maxl0 = tree[i].maxr0 = !a[l];
        tree[i].sum1 = tree[i].maxl1 = tree[i].maxr1 = a[l];
        return ;
    }
    int mid = (l + r) >> 1;
    Build (i << 1,l,mid);
    Build (i << 1 | 1,mid + 1,r);
    Push_up (i);
}
void Update (int i,int l,int r,int flag){//区间赋值为0或者1
    if (tree[i].l > r ||  tree[i].r < l) return;//区间不符,直接返回,不改了 
    if (tree[i].l >= l && tree[i].r <= r){
        tree[i].lazy1 = flag + 1;  
        /*flag记录原操作(因为lazy=0时为赋值0的操作,而清楚标记时将标记变成了0,
        Maintain函数中便将1当做赋0,2当做赋1)*/ 
        Maintain (i);
        return ;
    }
    Push_down (i);
    Update (i << 1,l,r,flag);
    Update (i << 1 | 1,l,r,flag);
    Push_up (i);
}
void Flip (int i,int l,int r){//区间翻转
    if (tree[i].l > r || l > tree[i].r) return;
    if (tree[i].l >= l && tree[i].r <= r){
        tree[i].overturn ^= 1;//就是搞不懂为什么要翻转标记 
        Mainflip (i);
        return;
    }
    Push_down (i);
    Flip (i << 1,l,r);
    Flip (i << 1 | 1,l,r);
    Push_up (i);
}
int query1 (int i,int l,int r){//查询总的1的个数
    if (tree[i].l > r || l > tree[i].r) return 0;
    if (tree[i].l >= l && tree[i].r <= r) return tree[i].sum1;
    Push_down (i);
    return query1 (i << 1,l,r) + query1 (i << 1 | 1,l,r); 
}
node query2 (int i,int l,int r){//最长的连续的1的长度
    if (l == tree[i].l && r == tree[i].r) return tree[i];
    Push_down (i);
    int mid = (tree[i].l + tree[i].r) >> 1;
    if (r <= mid) return query2 (i << 1,l,r);//在左区间
    else if (mid < l) return query2 (i << 1 | 1,l,r);//在右区间
    else{
        node ls = query2 (i << 1,l,mid),rs = query2 (i << 1 | 1,mid + 1,r),tmp;//递归
        tmp.l = tree[i].l,tmp.r = tree[i].r;
        tmp.maxl1 = (ls.maxl1 == ls.size() ? ls.maxl1 + rs.maxl1 : ls.maxl1);
        tmp.maxr1 = (rs.maxr1 == rs.size() ? rs.maxr1 + ls.maxr1 : rs.maxr1);//把两边的像Pushup揉成一坨
        tmp.max1 = Max (ls.max1,rs.max1,ls.maxr1 + rs.maxl1);//统计最大值
        return tmp;
    }
}
void read (int &x){ 
    x = 0;char c = getchar();int f = 1;
    while (c < '0' || c > '9') {if (c == '-') f = -f;c = getchar();}
    while (c >= '0' && c <= '9') {x = (x << 3) + (x << 1) + c - '0';c = getchar();}
    x *= f;
    return ;
}
void write (int x){
    if (x < 0){x = -x;putchar ('-');}
    if (x > 9) write (x / 10);
    putchar (x % 10 + '0');
}
signed main(){
    read (n),read (m);
    for (register int i = 1;i <= n;++ i) read (a[i]);
    Build (1,1,n);
    for (register int i = 1;i <= m;++ i){
        int opt,l,r;
        read (opt),read (l),read (r);
        l ++,r ++;
        if (opt <= 1) Update (1,l,r,opt);
        else if (opt == 2) Flip (1,l,r);
        else if (opt == 3) write (query1 (1,l,r)),putchar ('
');
        else if (opt == 4) write (query2 (1,l,r).max1),putchar ('
'); 
    } 
}
原文地址:https://www.cnblogs.com/aprincess/p/11625903.html