HDU-3911Black and White

Black And White

 

Description

There are a bunch of stones on the beach; Stone color is white or black. Little Sheep has a magic brush, she can change the color of a continuous stone, black to white, white to black. Little Sheep like black very much, so she want to know the longest period of consecutive black stones in a range [i, j].

 

Input

  There are multiple cases, the first line of each case is an integer n(1<= n <= 10^5), followed by n integer 1 or 0(1 indicates black stone and 0 indicates white stone), then is an integer M(1<=M<=10^5) followed by M operations formatted as x i j(x = 0 or 1) , x=1 means change the color of stones in range[i,j], and x=0 means ask the longest period of consecutive black stones in range[i,j]

 

Output

When x=0 output a number means the longest length of black stones in range [i,j].

 

Sample Input

4 1 0 1 0 5 0 1 4 1 2 3 0 1 4 1 3 3 0 4 4

 

Sample Output

1 2 0

 

Source

2011 Multi-University Training Contest 8 - Host by HUST

Submit Status

 

线段树的区间合并问题,本题变化只有0->11->0,查询是否有连续区间满足要求,在区间更改时用到lazy标记,本题只涉及01的转化,用异或操作最合适。

线段树每个节点维护三类值,从左数(左子树)最大连续1的个数,从右数(右子树)最大连续1的个数,中间最大连续1的个数(可能跨越左右子树)

同理还有维护0的值。每次更新后查询是否需要异或向下传递信息

#include <cstdio>

#include <cstring>

#include <cstdlib>

#include <iostream>

#include <algorithm>

using namespace std;

#define INF 0x3f3f3f3f

#define inf -0x3f3f3f3f

#define mem0(a) memset(a,0,sizeof(a))

const int maxn = 100000+10;

struct node{

    int l,r;

    int lsum0,lsum1;//一条线段从左边数最长连续0/1的个数

    int rsum0,rsum1;//一条线段从右边数最长连续0/1的个数

    int msum0,msum1;//这条线段最长连续0/1的个数

    int lazy;

}a[maxn<<2];

int b[maxn];

void pushup(int cur){//合并

    int l_len=a[cur<<1].r - a[cur<<1].l +1;//求左子树线段长度

    int r_len=a[cur<<1|1].r - a[cur<<1|1].l +1;//求右子树线段长度

 

    a[cur].lsum1 = a[cur<<1].lsum1;//求当前节点cur左边连续1的个数=左子树的左边连续1的个数

    if(a[cur<<1].lsum1 == l_len){//如果左子树连续1的个数等于左子树的长度,那么cur节点的左边

//连续1的个数还需加上右子树左边连续1的个数

        a[cur].lsum1+=a[cur<<1|1].lsum1;

    }

    a[cur].rsum1 = a[cur<<1|1].rsum1;//求当前节点cur右边连续1的个数=右子树右边连续1的个数

    if(a[cur<<1|1].rsum1 == r_len){//如果右子树连续1的个数等于右子树的长度,那么cur节点右边

//连续1的个数还需加上左子树右边连续1的个数

        a[cur].rsum1 +=a[cur<<1].rsum1;

    }

//cur节点最长连续1的个数,是左子树最长连续1的个数,右子树最长连续1的个数,(左子树右边连续1的个数+右子树左边连续1的个数)三者最大值

    a[cur].msum1 = max((a[cur<<1].rsum1+a[cur<<1|1].lsum1),max(a[cur<<1].msum1,a[cur<<1|1].msum1));

 

    a[cur].lsum0 = a[cur<<1].lsum0;

    if(a[cur<<1].lsum0 == l_len){

        a[cur].lsum0+=a[cur<<1|1].lsum0;

    }

    a[cur].rsum0 = a[cur<<1|1].rsum0;

    if(a[cur<<1|1].rsum0 == r_len){

        a[cur].rsum0+=a[cur<<1].rsum0;

    }

    a[cur].msum0 = max((a[cur<<1].rsum0+a[cur<<1|1].lsum0),max(a[cur<<1].msum0,a[cur<<1|1].msum0));

 

}

void build(int l,int r,int cur){//建立线段树

    a[cur].l = l ;

    a[cur].r = r;

    a[cur].lazy = 0;//初始化都未标记

    if(l==r){//更新到每个子叶节点,赋初始值(颜色)

        int tt;

        scanf("%d",&tt);

        if(tt==1){//子叶为黑色

            a[cur].lsum1 = a[cur].rsum1 = a[cur].msum1 =1;

            a[cur].lsum0 = a[cur].rsum0 = a[cur].msum0 =0;

        }

        else {

            a[cur].lsum1 = a[cur].rsum1 = a[cur].msum1 =0;

            a[cur].lsum0 = a[cur].rsum0 = a[cur].msum0 =1;

        }

        return ;

    }

    int mid = (l + r )>>1;

    build(l,mid,cur<<1);

    build(mid+1,r,cur<<1|1);

    pushup(cur);//向上更新

}

void pushdown(int cur){//lazy思想,向左右子树传递

    if(a[cur].lazy == 1){

        a[cur<<1].lazy ^= 1;

        a[cur<<1|1].lazy ^= 1;

        swap(a[cur<<1].lsum1,a[cur<<1].lsum0);

        swap(a[cur<<1].rsum1,a[cur<<1].rsum0);

        swap(a[cur<<1].msum1,a[cur<<1].msum0);

 

        swap(a[cur<<1|1].lsum1,a[cur<<1|1].lsum0);

        swap(a[cur<<1|1].rsum1,a[cur<<1|1].rsum0);

        swap(a[cur<<1|1].msum1,a[cur<<1|1].msum0);

        a[cur].lazy = 0;

    }

}

void update(int l,int r,int cur){

    if( l <= a[cur].l && r >= a[cur].r)//刚好是需要更新区间[l,r]

    {

        a[cur].lazy^=1;//反转

        swap(a[cur].lsum1,a[cur].lsum0);//更新cur节点代表线段的值

        swap(a[cur].rsum1,a[cur].rsum0);

        swap(a[cur].msum1,a[cur].msum0);

        return ;

    }

    pushdown(cur);//用到子树时在更新子树的值

    int mid = (a[cur].l + a[cur].r )>>1;

    if( r <= mid) update(l,r,cur<<1);

    else if( l > mid )update(l,r,cur<<1|1);

    else{

        update(l,mid,cur<<1);

        update(mid+1,r,cur<<1|1);

    }

    pushup(cur);

}

int query(int l,int r,int cur){

    if( l <= a[cur].l && r >= a[cur].r){//查询到所求区间

        return a[cur].msum1;

    }

    pushdown(cur);

    int mid = (a[cur].l +a[cur].r)>>1;

    if( r <= mid )

        return query(l,r,cur<<1);

    else if( l > mid )

        return query(l,r,cur<<1|1);

    else {

        int lr = query(l,mid,cur<<1);//左子树最大值

        int rr = query(mid+1,r,cur<<1|1);//右子树最大值

        int aa = a[cur<<1].rsum1;//左子树右边连续最大值

        if(aa > a[cur<<1].r - l +1)//个数小于等于[l,a[cur<<1].r]的长度

            aa = a[cur<<1].r-l+1;

        int bb = a[cur<<1|1].lsum1;//右子树左边连续最大值

        if( bb > r - a[cur<<1|1].l +1)//个数小于等于[a[cur].l,r]的长度

            bb = r - a[cur<<1|1].l+1;

        return max(max(lr,rr),aa+bb);//取左子树,右子树,中间三者的最大值

    }

}

int main()

{

    int n,m;

    while(scanf("%d",&n)!=EOF){

        build(1,n,1);

        scanf("%d",&m);

        while(m--){

            int a,b,c;

            scanf("%d%d%d",&a,&b,&c);

            if(a  == 0 ){

                printf("%d\n",query(b,c,1));

            }

            else

                update(b,c,1);

        }

    }

    return 0;

}

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值