HYSBZ-1858 序列操作(线段树)

题意

给定一个长度为 n 01 串,完成 m 个操作,操作分 5 种。
0. x y 把区间 [ x , y ] 全部赋值为 0
1. x y 把区间 [ x , y ] 全部赋值为 1
2. x y 对区间 [ x , y ] 取反,即 0 1 1 0
3. x y 询问区间 [ x , y ] 内总共有多少个 1
4. x y 询问区间 [ x , y ] 内最多有多少个连续的 1
1 n , m 10 5

思路

这种区间修改查询的问题,线段树是最适合的。先观察询问,总共有多少个 1 的询问,只用存储每个节点区间中 1 的个数,再累加上来即可。而最多连续多少个 1 的询问,先考虑把问题化小,一个区间中最多连续 1 的个数莫过于以下三种:左子区间最多连续 1 的个数(都在左边),右子区间最多连续 1 的个数(都在右边),以左子区间最右端向左的最多连续 1 的个数加右子区间向右的最多连续个数(横跨左右)。所以,对于一个区间我们要维护的信息有: 1 的个数,最多连续 1 的个数,最左端向右最多连续 1 的个数,最右端向左最多 1 的个数。
再看看修改。整体赋值的操作用一个代表赋值的标记就可以实现,而整体取反,再加一个代表取反的标记也可以实现(由于取反的存在,上述维护信息还要多维护关于 0 的信息)。但是再处理多标记的线段树时,一定要考虑标记之间是否会互相影响。比如这题,如果一个区间先赋值,再取反,赋值标记也应该取反;先取反,再赋值,取反标记应直接消失。

代码

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
#define N 100003
typedef long long LL;
using namespace std;

struct node
{
    int L,R,ls[2],rs[2],as[2],sum[2],tag;
    bool re;
    node operator +(const node &_)const
    {
        node res; 
        res.L=L,res.R=_.R,res.tag=-1,res.re=0;
        FOR(i,0,1)
        {
            res.ls[i]=(ls[i]==R-L+1?ls[i]+_.ls[i]:ls[i]);
            res.rs[i]=(_.rs[i]==_.R-_.L+1?_.rs[i]+rs[i]:_.rs[i]);
            res.as[i]=max(max(as[i],_.as[i]),rs[i]+_.ls[i]);
            res.sum[i]=sum[i]+_.sum[i];
        }
        return res;
    }
    void tag_up(int val)
    {
        re=0,tag=val;
        FOR(i,0,1)ls[i]=rs[i]=as[i]=sum[i]=(tag==i)*(R-L+1);
        return;
    }
    void re_up()
    {
        re^=1;
        if(tag==0)tag=1;
        else if(tag==1)tag=0;
        swap(ls[0],ls[1]),swap(rs[0],rs[1]),swap(as[0],as[1]),swap(sum[0],sum[1]);
        return;
    }
};
struct SegmentTree
{
    node nd[N<<2];
    void build(int k,int L,int R,int *arr)
    {
        if(L==R)
        {
            nd[k].L=nd[k].R=L;
            FOR(i,0,1)nd[k].ls[i]=nd[k].rs[i]=nd[k].as[i]=nd[k].sum[i]=(arr[L]==i);
            nd[k].tag=-1;
            nd[k].re=0;
            return;
        }
        build(k<<1,L,L+R>>1,arr);
        build(k<<1|1,(L+R>>1)+1,R,arr);
        nd[k]=nd[k<<1]+nd[k<<1|1];
        return;
    }
    void push_down(int k)
    {
        if(nd[k].re)
        {
            nd[k<<1].re_up();
            nd[k<<1|1].re_up();
            nd[k].re=0;
        }
        if(!~nd[k].tag)return;
        nd[k<<1].tag_up(nd[k].tag);
        nd[k<<1|1].tag_up(nd[k].tag);
        nd[k].tag=-1;
        return;
    }
    void update(int k,int L,int R,int val)
    {
        if(L<=nd[k].L&&nd[k].R<=R)
        {
            if(val!=2)nd[k].tag_up(val);
            else nd[k].re_up();
            return;
        }
        push_down(k);
        int mid=nd[k].L+nd[k].R>>1;
        if(L<=mid)update(k<<1,L,R,val);
        if(mid<R)update(k<<1|1,L,R,val);
        nd[k]=nd[k<<1]+nd[k<<1|1];
        return;
    }
    node query(int k,int L,int R)
    {
        if(L<=nd[k].L&&nd[k].R<=R)
            return nd[k];
        push_down(k);
        int mid=nd[k].L+nd[k].R>>1;
        if(R<=mid)return query(k<<1,L,R);
        else if(mid<L)return query(k<<1|1,L,R);
        else return query(k<<1,L,R)+query(k<<1|1,L,R);
    }
}ST;

int a[N];
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        FOR(i,1,n)scanf("%d",&a[i]);
        ST.build(1,1,n,a);
        FOR(i,1,m)
        {
            int op,x,y;
            scanf("%d%d%d",&op,&x,&y);
            x++,y++;
            if(op>=0&&op<=2)
                ST.update(1,x,y,op);
            else if(op==3)printf("%d\n",ST.query(1,x,y).sum[1]);
            else printf("%d\n",ST.query(1,x,y).as[1]);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Paulliant/article/details/81805487
今日推荐