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 |
线段树的区间合并问题,本题变化只有0->1或1->0,查询是否有连续区间满足要求,在区间更改时用到lazy标记,本题只涉及0和1的转化,用异或操作最合适。
线段树每个节点维护三类值,从左数(左子树)最大连续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;
}