【 简 明 题 意 】 : \color{blue}{【简明题意】:} 【简明题意】:
【 思 路 】 : \color{blue}{【思路】:} 【思路】: 像这种维护序列的题目,一般都可以用 线 段 树 \color{orange}{线段树} 线段树求解。
首先,我们来看看我们需要定义一些什么东西:
其实,本题的操作都不算很难,最难的操作是求区间内最长的连续的 1 1 1的个数。下面,我们来讲讲此操作。
首先,我们可以把问题拆分成以下几个情况:
讲的有点隐晦,我们来看代码吧:
#define gc getchar()
#define g(c) isdigit(c)
inline int read(){
char c=0;int x=0;bool f=0;
while (!g(c)) f=c=='-',c=gc;
while (g(c)) x=x*10+c-48,c=gc;
return f?-x:x;
}//以上是卡常用的快读
const int N=1e5+100;
int sumo[N<<2],sumz[N<<2],cono[N<<2];
int conz[N<<2],lz[N<<2],add[N<<2];
int rz[N<<2],lo[N<<2],ro[N<<2],len[N<<2];
//sumo:区间内1的个数
//sumz:区间内0的个数
//cono:区间内最多连续1的个数
//conz:区间内最多连续0的个数
// add:区间修改的懒标记(1:全改1;2:全改0;3:取反;0:不操作)
// lz:区间内左边连续0的个数
// rz:区间内右边连续0的个数
// lo:区间内左边连续1的个数
// ro:区间内右边连续1的个数
inline void pushup(int o){
sumo[o]=sumo[o<<1]+sumo[o<<1|1];sumz[o]=sumz[o<<1]+sumz[o<<1|1];
cono[o]=max(cono[o<<1],max(cono[o<<1|1],ro[o<<1]+lo[o<<1|1]));
conz[o]=max(conz[o<<1],max(conz[o<<1|1],rz[o<<1]+lz[o<<1|1]));
if (lo[o<<1]==len[o<<1])
lo[o]=lo[o<<1]+lo[o<<1|1];
else lo[o]=lo[o<<1];
if (ro[o<<1|1]==len[o<<1|1])
ro[o]=ro[o<<1]+ro[o<<1|1];
else ro[o]=ro[o<<1|1];
if (lz[o<<1]==len[o<<1])
lz[o]=lz[o<<1]+lz[o<<1|1];
else lz[o]=lz[o<<1];
if (rz[o<<1|1]==len[o<<1|1])
rz[o]=rz[o<<1]+rz[o<<1|1];
else rz[o]=rz[o<<1|1];
}
inline void inverse(int o){
add[o]=3-add[o];
swap(sumo[o],sumz[o]);
swap(cono[o],conz[o]);
swap(lz[o],lo[o]);
swap(rz[o],ro[o]);
}//区间的取反操作
inline void change_to_1(int o){
sumo[o]=cono[o]=lo[o]=ro[o]=len[o];
sumz[o]=conz[o]=lz[o]=rz[o]=0;
}//把区间o的所有数改为1
inline void change_to_0(int o){
sumo[o]=cono[o]=lo[o]=ro[o]=0;
sumz[o]=conz[o]=lz[o]=rz[o]=len[o];
}//把区间o的所有数改为0
inline void pushdown(int o){
int tag=add[o];add[o]=0;
if (tag==1){
change_to_1(o<<1);add[o<<1|1]=1;
change_to_1(o<<1|1);add[o<<1]=1;
}
else if (tag==2){
change_to_0(o<<1);add[o<<1|1]=2;
change_to_0(o<<1|1);add[o<<1]=2;
}
else if (tag==3){
inverse(o<<1);
inverse(o<<1|1);
}
}//标记下传操作
bool a[N];int n,m;
void build(int o,int l,int r){
add[o]=0;len[o]=r-l+1;
if (l==r){
if (a[l]) change_to_1(o);
else change_to_0(o);
return;
}
register int mid=(l+r)>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
pushup(o);return;
}//建树操作
void updata1(int o,int l,int r,int p,int q){
if (l>q||r<p) return;
if (p<=l&&r<=q){
change_to_1(o);
add[o]=1;return;
}
if (add[o]) pushdown(o);
register int mid=(l+r)>>1;
updata1(o<<1,l,mid,p,q);
updata1(o<<1|1,mid+1,r,p,q);
pushup(o);return;
}//操作2:把区间所有的数改为1
void updata2(int o,int l,int r,int p,int q){
if (l>q||r<p) return;
if (p<=l&&r<=q){
change_to_0(o);
add[o]=2;return;
}
if (add[o]) pushdown(o);
register int mid=(l+r)>>1;
updata2(o<<1,l,mid,p,q);
updata2(o<<1|1,mid+1,r,p,q);
pushup(o);return;
}//操作1:把区间所有的数改为0
void updata3(int o,int l,int r,int p,int q){
if (l>q||r<p) return;
if (p<=l&&r<=q){
inverse(o);
return;
}
if (add[o]) pushdown(o);
register int mid=(l+r)>>1;
updata3(o<<1,l,mid,p,q);
updata3(o<<1|1,mid+1,r,p,q);
pushup(o);return;
}//操作3:区间取反操作
int query1(int o,int l,int r,int p,int q){
if (l>q||r<p) return 0;
if (p<=l&&r<=q) return sumo[o];
if (add[o]) pushdown(o);
int mid=(l+r)>>1,ans=0;
ans+=query1(o<<1,l,mid,p,q);
ans+=query1(o<<1|1,mid+1,r,p,q);
return ans;
}//操作4:查询区间有多少个1
int query2(int o,int l,int r,int p,int q){
if (l>q||r<p) return 0;
if (p<=l&&r<=q) return cono[o];
if (add[o]) pushdown(o);
register int mid=(l+r)>>1,answer=0;
if (q<=mid) answer=query2(o<<1,l,mid,p,q);
else if (p>mid) answer=query2(o<<1|1,mid+1,r,p,q);
else{
answer=query2(o<<1|1,mid+1,r,p,q);
answer=max(answer,query2(o<<1,l,mid,p,q));
int Ro=min(ro[o<<1],mid-p+1);
int Lo=min(lo[o<<1|1],q-mid);
answer=max(answer,Ro+Lo);
}
return answer;
}//操作5:查询区间最长连续1的个数
//以上全部都是线段树的维护与查询
int main(){
freopen("t1.in","r",stdin);
n=read();m=read();
for(int i=1;i<=n;i++)
a[i]=read();
build(1,1,n);
for(int i=1;i<=m;i++){
int opt=read(),l=read()+1,r=read()+1;
switch(opt){
case 0:updata2(1,1,n,l,r);break;
case 1:updata1(1,1,n,l,r);break;
case 2:updata3(1,1,n,l,r);break;
case 3:printf("%d\n",query1(1,1,n,l,r));break;
default:printf("%d\n",query2(1,1,n,l,r));
}
}
return 0;
}