bzoj 1858 序列操作

有毒的线段树

一个01序列,支持以下操作

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 


先不考虑操作3

线段树的节点要维护

从左端点开始连续的1的个数 lmax

从右端点开始连续的1的个数 rmax

当前区间最长的连续的1的长度 ma

当前区间1的个数 sum

当前区间是否都是1 all

合并的时候不妨设左右区间分别为 L,R

如果 L.all lmax=L.sum+R.lmax

否则 lmax=L.lmax

rmax 同理

ma=max(max(L.ma,R.ma),L.rmax+R.lmax)

弱化的题目大概这样就可以了


然后取反的怎么做呢?

是不是同时维护一下对于0的这五个值,取反的时候直接交换就可以了

同时我们发现当前区间都是1等价于0的个数为0,所以维护四个值就好

另外值得注意的是这里线段树支持的两个操作(取反,覆盖)是不独立的

也就是取反的时候不能直接覆盖lazy,而要由原来的lazy计算新的lazy

具体见代码

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

const int INF = 0x3f3f3f3f;
const int maxn = 112345;

struct Info{
    int v[2][4]; // lma max rma sum
    void maintain(int val){
        if(val<=1){
            int all = v[0][3]+v[1][3];
            for(int i=0;i<4;i++)
                v[1^val][i] = 0,v[val][i] = all;
        }
        else{
            for(int i=0;i<4;i++) swap(v[0][i],v[1][i]);
        }
    }
    void clear(){
        memset(v,0,sizeof(v));
    }
    void init(){
        memset(v,0,sizeof(v));
        for(int i=0;i<4;i++)
            v[0][i] = 1;
    }
    void out(){
        puts("----------------------st");
        for(int i=0;i<=1;i++){
            printf("%d : (ls %d ma %d rs %d) sum = %d\n",i,v[i][0],v[i][1],v[i][2],v[i][3]);
        }
        puts("-----------------------here");
    }
};

Info operator + (const Info & L,const Info & R){
    Info ret;
    ret.clear();
    for(int i=0;i<=1;i++){
        ret.v[i][3] = L.v[i][3] + R.v[i][3];
        ret.v[i][0] = L.v[i][0]; if(L.v[i^1][3]==0) ret.v[i][0] += R.v[i][0];
        ret.v[i][2] = R.v[i][2]; if(R.v[i^1][3]==0) ret.v[i][2] += L.v[i][2];
        ret.v[i][1] = max(L.v[i][1],R.v[i][1]);
        ret.v[i][1] = max(ret.v[i][1],L.v[i][2]+R.v[i][0]);
    }
    return ret;
}

#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define root 1,1,n
#define Now int o,int l,int r
#define Mid int m=l + (r-l)/2

Info val[maxn*4];
int lazy[maxn*4];

int chag(int x){
    if(x==-1) return 2;
    if(x<2) return x^1;
    return -1;
}

void push_down(Now){
    int v = lazy[o];
    lazy[o] = -1;
    if(l==r) return;
    val[o<<1].maintain(v);
    val[o<<1|1].maintain(v);
    if(v<=1){
        lazy[o<<1] = lazy[o<<1|1] = v;
    }
    else{
        lazy[o<<1] = chag(lazy[o<<1]);
        lazy[o<<1|1] = chag(lazy[o<<1|1]);
    }
}

void update(Now,int ul,int ur,int v){
    if(ul <= l && r <= ur){
        if(v<=1) lazy[o] = v;
        else     lazy[o] = chag(lazy[o]);
        val[o].maintain(v);
        return;
    }
    Mid;
    if(lazy[o]!=-1) push_down(o,l,r);
    if(ul <= m){
        update(lson,ul,ur,v);
    }
    if(m+1<=ur){
        update(rson,ul,ur,v);
    }
    val[o] = val[o<<1] + val[o<<1|1];
}

Info query(Now,int ql,int qr){
    if(ql <= l && r <= qr){
        return val[o];
    }
    Info ret;
    ret.clear();
    if(lazy[o]!=-1){
        push_down(o,l,r);
    }
    Mid;
    if(ql <= m)ret = query(lson,ql,qr) + ret;
    if(m+1<=qr)ret = ret + query(rson,ql,qr);
    return ret;
}

void buildtree(Now){
    if(l!=r){
        Mid;
        buildtree(lson);
        buildtree(rson);
        val[o] = val[o<<1] + val[o<<1|1];
    }
    else val[o].init();
}

int main(){
    int n,m;
    while(~scanf("%d %d",&n,&m)){
        int v;
        memset(lazy,-1,sizeof(lazy));
        buildtree(root);
        for(int i=1;i<=n;i++){
            scanf("%d",&v);
            update(root,i,i,v);
        }
        int l,r;
        while(m--){
            scanf("%d %d %d",&v,&l,&r);
            l++,r++;
            if(v<=2) update(root,l,r,v);
            else{
                if(v == 3) printf("%d\n",query(root,l,r).v[1][3]);
                else       printf("%d\n",query(root,l,r).v[1][1]);
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值