【BZOJ1858】【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)

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

HINT

对于30%的数据,1<=n, m<=1000;
对于100%的数据,1<=n, m<=100000。

题解:
本来以为是一道比较简单的线段树打打标记就过了,但是询问4好难写。。。
其他的比较简单,就是一些tag,rev标记。主要说一下询问4。
我们需要维护下列东西:
1.1的个数;(sum1)
2.从头开始1的连续的个数;(l1)
3.从尾开始1的连续的个数(2、3两个是为了维护连续最大值而存在的);(r1)
4.最大的连续的1的个数;(mx1)
5.从头开始0的连续的个数;(l0)
6.从尾开始0的连续的个数;(l1)
7.最大的连续的0的个数;(mx0)
8.0的个数;(sum0)

对于询问4,只是查询答案显然无法做到最值的合并与比较,那么,我们就直接询问线段树该节点的状态,直接返回一棵临时的线段树,就可以知道连续的最值了。这里要用到线段树的合并,也算一项技能,可以多看看。

代码如下:

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<math.h>
#define ll long long
#define inf 0x7f7f7f7f
#define N 100005 
#define ls (rt<<1)
#define rs (rt<<1|1)
#define mid (l+r>>1)
using namespace std;
struct seg
{
    int l,r;
    int l0,l1,r0,r1,mx0,mx1,sum0,sum1;
    int rev,c,full;
}t[N*3];
void color(int rt,int c)
{
    t[rt].full=c;t[rt].rev=0;
    int len=t[rt].r-t[rt].l+1;
    if(!c)
    {
        t[rt].sum0=t[rt].l0=t[rt].r0=t[rt].mx0=len;
        t[rt].sum1=t[rt].l1=t[rt].r1=t[rt].mx1=0;
    }
    else
    {
        t[rt].sum0=t[rt].l0=t[rt].r0=t[rt].mx0=0;
        t[rt].sum1=t[rt].l1=t[rt].r1=t[rt].mx1=len;
    }
}
void rev(int rt)
{
    swap(t[rt].sum0,t[rt].sum1);
    swap(t[rt].l0,t[rt].l1);
    swap(t[rt].r0,t[rt].r1);
    swap(t[rt].mx0,t[rt].mx1);
    if(~t[rt].full) t[rt].full^=1;
}
seg merge(seg a,seg b)
{
    seg tmp;
    tmp.l=a.l,tmp.r=b.r;
    tmp.rev=0,tmp.c=-1;
    tmp.l0=a.l0,tmp.l1=a.l1,tmp.r0=b.r0,tmp.r1=b.r1;
    tmp.mx0=max(a.mx0,b.mx0),tmp.mx1=max(a.mx1,b.mx1);
    tmp.mx0=max(tmp.mx0,a.r0+b.l0),tmp.mx1=max(tmp.mx1,a.r1+b.l1);
    tmp.sum0=a.sum0+b.sum0,tmp.sum1=a.sum1+b.sum1;
    if(a.full==0) tmp.l0=a.mx0+b.l0;
    else if(a.full==1) tmp.l1=a.mx1+b.l1;
    if(b.full==0) tmp.r0=b.mx0+a.r0;
    else if(b.full==1) tmp.r1=b.mx1+a.r1;
    if(a.full==b.full) tmp.full=a.full;
    else tmp.full=-1;
    return tmp;
}
void pushup(int rt){t[rt]=merge(t[ls],t[rs]);}
void pushdown(int rt)
{
    if(t[rt].l==t[rt].r) return;
    if(~t[rt].c)
    {
        t[ls].c=t[rs].c=t[rt].c;
        color(ls,t[rt].c),color(rs,t[rt].c);
        t[rt].c=-1;
    }
    if(t[rt].rev)
    {
        t[ls].rev^=1;t[rs].rev^=1;
        rev(ls),rev(rs);
        t[rt].rev=0;
    }
}
void build(int rt,int l,int r)
{
    t[rt].l=l,t[rt].r=r;
    t[rt].c=-1;
    if(l==r)
    {
        scanf("%d",&t[rt].full);
        if(t[rt].full) t[rt].l1=t[rt].r1=t[rt].mx1=t[rt].sum1=1;
        else t[rt].l0=t[rt].r0=t[rt].mx0=t[rt].sum0=1;
        return ;
    }
    build(ls,l,mid);build(rs,mid+1,r);
    pushup(rt);
}
void change(int rt,int L,int R,int v)
{
    pushdown(rt);
    int l=t[rt].l,r=t[rt].r;
    if(l==L && r==R)
    {
        color(rt,v);
        t[rt].c=v;
        return ;
    }
    if(R<=mid) change(ls,L,R,v);
    else if(L>mid) change(rs,L,R,v);
    else{change(ls,L,mid,v);change(rs,mid+1,R,v);}
    pushup(rt);
}
void rever(int rt,int L,int R)
{
    pushdown(rt);
    int l=t[rt].l,r=t[rt].r;
    if(l==L && r==R)
    {
        rev(rt);
        t[rt].rev=1;
        return ;
    }
    if(R<=mid) rever(ls,L,R);
    else if(L>mid) rever(rs,L,R);
    else{rever(ls,L,mid);rever(rs,mid+1,R);}
    pushup(rt);
}
int getsum(int rt,int L,int R)
{
    pushdown(rt);
    int l=t[rt].l,r=t[rt].r;
    if(l==L && r==R) return t[rt].sum1;
    if(R<=mid) return getsum(ls,L,R);
    else if(L>mid) return getsum(rs,L,R);
    else return getsum(ls,L,mid)+getsum(rs,mid+1,R);
}
seg ask(int rt,int L,int R)
{
    pushdown(rt);
    int l=t[rt].l,r=t[rt].r;
    if(l==L && r==R) return t[rt];
    if(R<=mid) return ask(ls,L,R);
    else if(L>mid) return ask(rs,L,R);
    else return merge(ask(ls,L,mid),ask(rs,mid+1,R));
}
int main()
{
    int n,m,op,a,b;
    scanf("%d%d",&n,&m);
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&op,&a,&b);
        a++,b++;
        switch(op)
        {
            case 0:change(1,a,b,0);break;
            case 1:change(1,a,b,1);break;
            case 2:rever(1,a,b);break;
            case 3:printf("%d\n",getsum(1,a,b));break;
            case 4:printf("%d\n",ask(1,a,b).mx1);break;
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值