• 序列操作


    题目描述

    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 ('
    '); 
        } 
    }
  • 相关阅读:
    js与jquery常用数组方法总结
    js 对象深复制,创建对象和继承
    Web前端面试常识
    jQuery插件开发之boxScroll与marquee
    jQuery插件开发之windowScroll
    《将博客搬至CSDN》
    蓝桥杯 翻硬币
    AcWing 756.蛇形矩阵
    货仓选址
    费解的开关
  • 原文地址:https://www.cnblogs.com/aprincess/p/11625903.html
Copyright © 2020-2023  润新知