HYSBZ 1858(Scoi2010) 序列操作(线段树+区间合并)

序列操作

Description

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都需要给出回答,聪明的程序员们,你们能帮助他吗?

Input

输入数据第一行包括2个数,n和m,分别表示序列的长度和操作数目 第二行包括n个数,表示序列的初始状态 接下来m行,每行3个数,op, a, b,(0<=op<=4,0<=a<=b<n)表示对于区间[a, b]执行标号为op的操作="" <="" iv="">

Output

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

Sample Input

 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

Sample Output

 5
2
6
5

解题思路:

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

AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <algorithm>
using namespace std;

const int maxn = 100005;

struct node{
    int l,r;
    int l1,r1;   //左右连续1
    int l0,r0;   //左右连续0
    int change;  //-1 无操作 2取反,0全0 1全1
    int sum1,sum0;  //最大连续1,0
    int all1,all0;  //所有1,0
}tree[maxn*3];
int a[maxn];

//由下向上合并
//tree[id]的延迟标记是指延迟tree[id]的两个孩子的操作,而tree[id]已经完成该操作
void pushup(int id){
    int lLen = tree[id<<1].r-tree[id<<1].l+1;
    int rLen = tree[id<<1|1].r-tree[id<<1|1].l+1;

    tree[id].l1 = tree[id<<1].l1;
    if(tree[id<<1].l1 == lLen)
        tree[id].l1 += tree[id<<1|1].l1;//左连续1的个数
    tree[id].r1 = tree[id<<1|1].r1;
    if(tree[id<<1|1].r1 == rLen)
        tree[id].r1 += tree[id<<1].r1;//右连续1的个数
    tree[id].sum1 = max(tree[id<<1].sum1,tree[id<<1|1].sum1);
    tree[id].sum1 = max(tree[id].sum1,tree[id<<1].r1+tree[id<<1|1].l1);//最大连续1=max(左边,右边,中间合并)
    tree[id].all1 = tree[id<<1].all1+tree[id<<1|1].all1;   //所有1

    tree[id].l0 = tree[id<<1].l0;
    if(tree[id<<1].l0 == lLen)
        tree[id].l0 += tree[id<<1|1].l0;
    tree[id].r0 = tree[id<<1|1].r0;
    if(tree[id<<1|1].r0 == rLen)
        tree[id].r0 += tree[id<<1].r0;
    tree[id].sum0 = max(tree[id<<1].sum0,tree[id<<1|1].sum0);
    tree[id].sum0 = max(tree[id].sum0,tree[id<<1].r0+tree[id<<1|1].l0);
    tree[id].all0 = tree[id<<1].all0+tree[id<<1|1].all0;
}

void pushdown(int id){
    if(tree[id].change != -1){
        int lLen = tree[id<<1].r-tree[id<<1].l+1;
        int rLen = tree[id<<1|1].r-tree[id<<1|1].l+1;
        if(tree[id].change == 0){  //set all 0
            tree[id<<1].change = tree[id<<1|1].change = 0;
            tree[id].change = -1;
            tree[id<<1].l1 = tree[id<<1].r1 = tree[id<<1].sum1 = tree[id<<1].all1=0;
            tree[id<<1].l0 = tree[id<<1].r0 = tree[id<<1].sum0 = tree[id<<1].all0 = lLen;
            tree[id<<1|1].l1 = tree[id<<1|1].r1 = tree[id<<1|1].sum1 = tree[id<<1|1].all1 = 0;
            tree[id<<1|1].l0 = tree[id<<1|1].r0 = tree[id<<1|1].sum0 = tree[id<<1|1].all0 = rLen;
            return ;
        }
        if(tree[id].change == 1){//set all 1
            tree[id<<1].change = tree[id<<1|1].change = 1;
            tree[id].change = -1;
            tree[id<<1].l1 = tree[id<<1].r1 = tree[id<<1].sum1 = tree[id<<1].all1=lLen;
            tree[id<<1].l0 = tree[id<<1].r0 = tree[id<<1].sum0 = tree[id<<1].all0=0;
            tree[id<<1|1].l1 = tree[id<<1|1].r1 = tree[id<<1|1].sum1 = tree[id<<1|1].all1=rLen;
            tree[id<<1|1].l0 = tree[id<<1|1].r0 = tree[id<<1|1].sum0 = tree[id<<1|1].all0=0;
            return ;
        }
        if(tree[id].change == 2){//0->1 1->0
            tree[id].change = -1;
            if(tree[id<<1].change == -1){//如果tree[id<<1]没有操作,则直接取反
                tree[id<<1].change = 2;
            }
            else if(tree[id<<1].change==0){//如果tree[id<<1]已经标记为置0,取反后为置1
                tree[id<<1].change = 1;
            }
            else if(tree[id<<1].change == 1){//如果tree[id<<1]已经标记为置1,取反后为置0
                tree[id<<1].change = 0;
            }
            else if(tree[id<<1].change == 2){//如果tree[id<<1]已经取反,再次取反相当于没操作
                tree[id<<1].change = -1;
            }
            swap(tree[id<<1].l0,tree[id<<1].l1); //tree[id<<1]进行取反操作,0,1的标记都要互换
            swap(tree[id<<1].r0,tree[id<<1].r1);
            swap(tree[id<<1].sum0,tree[id<<1].sum1);
            swap(tree[id<<1].all0,tree[id<<1].all1);
            //id<<1|1 同 id<<1
            if (tree[id<<1|1].change == -1){
                tree[id<<1|1].change = 2;
            }
            else if(tree[id<<1|1].change == 0){
                tree[id<<1|1].change = 1;
            }
            else if (tree[id<<1|1].change == 1){
                tree[id<<1|1].change = 0;
            }
            else if (tree[id<<1|1].change == 2){
                tree[id<<1|1].change = -1;
            }
            swap(tree[id<<1|1].l0,tree[id<<1|1].l1);
            swap(tree[id<<1|1].r0,tree[id<<1|1].r1);
            swap(tree[id<<1|1].sum0,tree[id<<1|1].sum1);
            swap(tree[id<<1|1].all0,tree[id<<1|1].all1);
        }
    }
}

void build(int id,int l,int r){
    tree[id].l = l;
    tree[id].r = r;
    tree[id].change = -1;
    if(l==r){
        tree[id].l1 = tree[id].r1 = tree[id].sum1 = tree[id].all1 = a[l];
        tree[id].l0 = tree[id].r0 = tree[id].sum0 = tree[id].all0 = 1-a[l];
        return ;
    }
    int mid = (tree[id].l+tree[id].r)>>1;
    build(id<<1,l,mid);
    build(id<<1|1,mid+1,r);
    pushup(id);
}

void update(int id,int l,int r,int op){
    if(tree[id].l == l && tree[id].r == r){
        //如果是2号操作,需要考虑原来的操作
        if(op == 2){
            if(tree[id].change == -1)
                tree[id].change = 2;
            else if(tree[id].change == 0)
                tree[id].change = 1;
            else if(tree[id].change == 1)
                tree[id].change = 0;
            else if(tree[id].change == 2)
                tree[id].change = -1;
        }else{
        //置0,1操作和原来的操作没关系
            tree[id].change = op;
        }
        if(op == 0){
        //该区间置0
            tree[id].l1 = tree[id].r1 = tree[id].sum1 = tree[id].all1 = 0;
            tree[id].l0 = tree[id].r0 = tree[id].sum0 = tree[id].all0 = tree[id].r-tree[id].l+1;
        }else if(op == 1){
        //该区间置1
            tree[id].l1 = tree[id].r1 = tree[id].sum1 = tree[id].all1 = tree[id].r-tree[id].l+1;
            tree[id].l0 = tree[id].r0 = tree[id].sum0 = tree[id].all0 = 0;
        }
        else if(op==2){
        //该区间取反
            swap(tree[id].l0,tree[id].l1);
            swap(tree[id].r0,tree[id].r1);
            swap(tree[id].sum0,tree[id].sum1);
            swap(tree[id].all0,tree[id].all1);
        }
        return ;
    }
    pushdown(id); //如果操作的区间为当前区间的子区间,则要把当前区间的change传到子区间
    int mid = (tree[id].l+tree[id].r)>>1;
    if(r <= mid)
        update(id<<1,l,r,op);
    else{
        if(l > mid)
            update(id<<1|1,l,r,op);
        else{
            update(id<<1,l,mid,op);
            update(id<<1|1,mid+1,r,op);
        }
    }
    pushup(id);//子区间修改完成后,要向父区间合并信息
}

//op == 1求所有1 op == 0求最大连续1
int query(int id,int l,int r,int op){
    if(tree[id].l == l && tree[id].r == r){
        if(op == 1)
            return tree[id].all1;
        return tree[id].sum1;
    }
    pushdown(id);
    int mid = (tree[id].l+tree[id].r)>>1;
    if(r <= mid)
        return query(id<<1,l,r,op);
    else{
        if(l > mid)
            return query(id<<1|1,l,r,op);
        else{
            if(op == 1)
                return query(id<<1,l,mid,op)+query(id<<1|1,mid+1,r,op);
            int ans,ans1,ans2;
            ans1 = query(id<<1,l,mid,op);
            ans2 = query(id<<1|1,mid+1,r,op);
            ans = max(ans1,ans2);
            ans = max(ans,min(tree[id<<1].r1,mid-l+1)+min(tree[id<<1|1].l1,r-mid)); //r-mid=r-(mid+1)+1
            return ans;
        }
    }
}

int main(){
    int n,q;
    while(~scanf("%d%d",&n,&q)){
        for(int i = 1; i <= n; i++)
            scanf("%d",&a[i]);
        build(1,1,n);
        while(q--){
            int op,x,y;
            scanf("%d%d%d",&op,&x,&y);
            x++;
            y++;
            if(op == 0)
                update(1,x,y,0);
            else if(op == 1)
                update(1,x,y,1);
            else if(op == 2)
                update(1,x,y,2);
            else if(op == 3){
                int ans = query(1,x,y,1);
                printf("%d\n",ans);
            }
            else if(op == 4){
                int ans = query(1,x,y,0);
                printf("%d\n",ans);
            }
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值