大概考的就是一个tag标记的先后顺序和相互影响的问题
这道题涉及到两个tag,一个是set,一个是翻转rev,假设先rev,set后入,set直接覆盖掉rev即可;假设是先set再rev,rev直接修改set标记。
pushdown的顺序:
因为有set函数保证之前的rev已清空,如果有rev标记则一定是后来的rev,所以先进行set,然而这里的set还应该覆盖掉子节点的rev,否则子节点的rev保留会被认为是set之后的rev,答案会出错
其次合并区间前后缀,注意同时维护0,1的相关值,rev时直接交换即可。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int maxn=100005;
struct tree
{
int sum,pre0,pre1,sub0,sub1,set,len0,len1,rev;
}t[maxn<<2];
void maintain(int ro,int l,int r)
{
t[ro].sum=t[ro<<1].sum+t[ro<<1|1].sum;
int mid=l+r>>1;
t[ro].sub1=t[ro<<1|1].sub1;
if(t[ro<<1|1].sub1==r-mid)t[ro].sub1+=t[ro<<1].sub1;
t[ro].pre1=t[ro<<1].pre1;
if(t[ro<<1].pre1==mid-l+1)t[ro].pre1+=t[ro<<1|1].pre1;
t[ro].sub0=t[ro<<1|1].sub0;
if(t[ro<<1|1].sub0==r-mid)t[ro].sub0+=t[ro<<1].sub0;
t[ro].pre0=t[ro<<1].pre0;
if(t[ro<<1].pre0==mid-l+1)t[ro].pre0+=t[ro<<1|1].pre0;
t[ro].len1=max(t[ro<<1].sub1+t[ro<<1|1].pre1,max(t[ro<<1].len1,t[ro<<1|1].len1));
t[ro].len0=max(t[ro<<1].sub0+t[ro<<1|1].pre0,max(t[ro<<1].len0,t[ro<<1|1].len0));
}
void rever(int ro)
{
swap(t[ro].len0,t[ro].len1);
swap(t[ro].sub0,t[ro].sub1);
swap(t[ro].pre0,t[ro].pre1);
}
void pushdown(int ro,int l,int r)
{
int mid=l+r>>1;
if(t[ro].set==0)
{
t[ro<<1].set=t[ro<<1|1].set=t[ro].set;
t[ro<<1].sub0=t[ro<<1].pre0=mid-l+1;
t[ro<<1|1].sub0=t[ro<<1|1].pre0=r-mid;
t[ro<<1].sub1=t[ro<<1].pre1=t[ro<<1|1].sub1=t[ro<<1|1].pre1=0;
t[ro<<1].sum=t[ro<<1|1].sum=0;
t[ro<<1].len0=mid-l+1;
t[ro<<1|1].len0=r-mid;
t[ro<<1].len1=t[ro<<1|1].len1=0;
t[ro<<1].rev=t[ro<<1|1].rev=0;//千万记得需要把子节点的翻转标记全部置零
t[ro].set=-1;
}
else if(t[ro].set==1)
{
t[ro<<1].set=t[ro<<1|1].set=t[ro].set;
t[ro<<1].sum=t[ro<<1].sub1=t[ro<<1].pre1=mid-l+1;
t[ro<<1|1].sum=t[ro<<1|1].sub1=t[ro<<1|1].pre1=r-mid;
t[ro<<1].sub0=t[ro<<1].pre0=t[ro<<1|1].sub0=t[ro<<1|1].pre0=0;
t[ro<<1].len1=mid-l+1;
t[ro<<1|1].len1=r-mid;
t[ro<<1].len0=t[ro<<1|1].len0=0;
t[ro<<1].rev=t[ro<<1|1].rev=0;
t[ro].set=-1;
}
if(t[ro].rev)
{
t[ro<<1].rev^=1;
t[ro<<1|1].rev^=1;
rever(ro<<1),rever(ro<<1|1);
t[ro<<1].sum=(mid-l+1-t[ro<<1].sum);
t[ro<<1|1].sum=(r-mid-t[ro<<1|1].sum);
t[ro].rev=0;
}
}
void build(int ro,int l,int r)
{
t[ro].set=-1;
if(l==r)
{
int tmp;
scanf("%d",&tmp);
if(tmp==0)
t[ro].len0=t[ro].pre0=t[ro].sub0=1;
else if(tmp==1)
t[ro].len1=t[ro].pre1=t[ro].sub1=t[ro].sum=1;
return;
}
int mid=l+r>>1;
build(ro<<1,l,mid);
build(ro<<1|1,mid+1,r);
maintain(ro,l,r);
}
void setv(int ro,int L,int R,int set,int l,int r)
{
if(L==l&&R==r)
{
t[ro].rev=0;//注意这里清空了rev,详见reverse函数注释
t[ro].set=set;
if(set==0)
{
t[ro].len1=t[ro].sum=t[ro].sub1=t[ro].pre1=0;
t[ro].len0=t[ro].sub0=t[ro].pre0=r-l+1;
}
else
{
t[ro].len1=t[ro].sum=t[ro].sub1=t[ro].pre1=r-l+1;
t[ro].len0=t[ro].sub0=t[ro].pre0=0;
}
return;
}
pushdown(ro,l,r);
int mid=l+r>>1;
if(R<=mid)setv(ro<<1,L,R,set,l,mid);
else if(L>=mid+1)setv(ro<<1|1,L,R,set,mid+1,r);
else setv(ro<<1,L,mid,set,l,mid),setv(ro<<1|1,mid+1,R,set,mid+1,r);
maintain(ro,l,r);
}
void reverse(int ro,int L,int R,int l,int r)
{
if(L==l&&R==r)
{
//若之前有set标记,则rev一定被清空(见setv函数)
//则rev是在清空的基础上进行叠加,所以pushdown中按时间顺序先set再rev
//但是这里需要注意一个问题,即这里的前提是set提前把rev清空
//而子节点很明显还是有rev标记,所以set更新时先清空rev再进行累加的rev操作
if(t[ro].set!=-1)t[ro].set^=1;
else t[ro].rev^=1;
//若在这里直接给rev取反不管set也是可以的,即set完毕之后rev再翻转
//若进行set标记的思路是直接翻转set标记等价于进行一次翻转,而rev少翻转一次,结果是不变的
rever(ro);
t[ro].sum=(r-l+1-t[ro].sum);
return;
}
pushdown(ro,l,r);
int mid=l+r>>1;
if(R<=mid)reverse(ro<<1,L,R,l,mid);
else if(L>=mid+1)reverse(ro<<1|1,L,R,mid+1,r);
else reverse(ro<<1,L,mid,l,mid),reverse(ro<<1|1,mid+1,R,mid+1,r);
maintain(ro,l,r);
}
int query_len(int ro,int L,int R,int l,int r)
{
if(L==l&&R==r)
return t[ro].len1;
pushdown(ro,l,r);
int mid=l+r>>1;
if(R<=mid)return query_len(ro<<1,L,R,l,mid);
else if(L>=mid+1)return query_len(ro<<1|1,L,R,mid+1,r);
else return max(min(mid-L+1,t[ro<<1].sub1)+min(R-mid,t[ro<<1|1].pre1),max(query_len(ro<<1,L,mid,l,mid),query_len(ro<<1|1,mid+1,R,mid+1,r)));
}
int query_sum(int ro,int L,int R,int l,int r)
{
if(L==l&&R==r)
return t[ro].sum;
pushdown(ro,l,r);
int mid=l+r>>1;
if(R<=mid)return query_sum(ro<<1,L,R,l,mid);
else if(L>=mid+1)return query_sum(ro<<1|1,L,R,mid+1,r);
else return query_sum(ro<<1,L,mid,l,mid)+query_sum(ro<<1|1,mid+1,R,mid+1,r);
}
int main()
{
int n,q;
scanf("%d%d",&n,&q);
build(1,1,n);
while(q--)
{
int d,d1,d2;
scanf("%d%d%d",&d,&d1,&d2);
d1++,d2++;
if(d<=1)
setv(1,d1,d2,d,1,n);
else if(d==2)
reverse(1,d1,d2,1,n);
else if(d==3)
printf("%d\n",query_sum(1,d1,d2,1,n));
else
printf("%d\n",query_len(1,d1,d2,1,n));
}
return 0;
}