刚开始学习线段树,没想到就碰到这样的题,由于太弱,搞了整整一天和一早上,真是充满了血与泪,但起码是自己一点点写出来的,值得。另外,由此题注意到写数据结构的题,代码的风格重要,这方面也要下点功夫。因为一个细节DEBUG花了好久,数据有些繁琐,细节比较多。下面是代码。
/*
结点需要保存的变量有lmax1,rmax1,sum1,mx1.分别代表从左数最大连续1的个数,从右数最大连续1的个数,1的总个数,期中最大的1连续个数.
lmax0....同理
mode 代表延迟标记的状态,这里我定义0:无,1:操作0,2:操作1,3:操作2.
L,R则代表结点区间范围。
*/
#include <iostream>
using namespace std;
int data[200000];
struct node{
int L,R;
int lmax1,rmax1,sum1,mx1;
int lmax0,rmax0,sum0,mx0;
int mode;
int dis(){return R-L+1;} //节点的区间大小
int mid(){return (L+R)/2;} //取中
void set_1(){lmax1=dis(),rmax1=dis(),sum1=dis(),mx1=dis(),lmax0=0,rmax0=0,sum0=0,mx0=0;} //操作1
void set_0(){lmax0=dis(),rmax0=dis(),sum0=dis(),mx0=dis(),lmax1=0,rmax1=0,sum1=0,mx1=0;} //操作0
void change(){swap(lmax0,lmax1),swap(rmax0,rmax1),swap(mx1,mx0),swap(sum0,sum1);} //操作2 :交换0和1的数据即可
};
node K[4*200000];
void push(int rt){ //向上更新操作
int q=rt<<1;
int p=q+1;
K[rt].lmax1=K[q].lmax1; K[rt].rmax1=K[p].rmax1;
K[rt].lmax0=K[q].lmax0; K[rt].rmax0=K[p].rmax0;
K[rt].sum0=K[q].sum0+K[p].sum0;
K[rt].sum1=K[q].sum1+K[p].sum1;
K[rt].mx1=max(max(K[q].mx1,K[p].mx1),K[q].rmax1+K[p].lmax1); //这里以下是能够区间合并时需要更新的数据
K[rt].mx0=max(max(K[q].mx0,K[p].mx0),K[q].rmax0+K[p].lmax0);
if(K[q].lmax0==K[q].dis()) K[rt].lmax0=K[q].dis()+K[p].lmax0;
if(K[p].rmax0==K[p].dis()) K[rt].rmax0=K[p].dis()+K[q].rmax0;
if(K[q].lmax1==K[q].dis()) K[rt].lmax1=K[q].dis()+K[p].lmax1;
if(K[p].rmax1==K[p].dis()) K[rt].rmax1=K[p].dis()+K[q].rmax1;
return ;
}
void build(int l,int r,int rt){
K[rt].mode=0;
K[rt].L=l;
K[rt].R=r;
if(l==r){
if(data[l]==0){K[rt].set_0();}
else{K[rt].set_1();}
return;
}
int m=K[rt].mid();
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
push(rt);
return ;
}
void trans(int transmode,int rt){ //操作函数
if(transmode==1){
K[rt].set_0();
}
else if(transmode==2){
K[rt].set_1();
}
else if(transmode==3){
K[rt].change();
}
return;
}
void modechan(int son,int fa){
if(K[fa].mode==3){
K[son].mode=K[fa].mode-K[son].mode;//3-3=0,3-1=2,3-2=1 因此正好可以用一个减法。(或者可以用两个延迟标记来区分操作0,1与操作2)
}
else
K[son].mode=K[fa].mode;
return ;
}
void updata(int M,int L,int R,int rt){ //更新操作
if(K[rt].L>=L&&K[rt].R<=R)
{
if(M==2&&K[rt].mode){ //这里表示在原来有延迟标记并且要赋予操作2的情况该如何转化
trans(M+1,rt); //先把该结点更新
K[rt].mode=M/K[rt].mode;
}
else{ //原来没有延迟标记或要赋予的不是操作2,直接覆盖。
trans(M+1,rt);
K[rt].mode=M+1;
}
return ;
}
if(K[rt].mode){
trans(K[rt].mode,rt<<1);
trans(K[rt].mode,rt<<1|1);
modechan(rt<<1,rt);
modechan(rt<<1|1,rt);
K[rt].mode=0;
}
int m=K[rt].mid();
if(R<=m)
updata(M,L,R,rt<<1);
else if(L>m)
updata(M,L,R,rt<<1|1);
else
{
updata(M,L,R,rt<<1);
updata(M,L,R,rt<<1|1);
}
push(rt);
return;
}
int query(int M,int L,int R,int rt){
if(K[rt].L>=L&&K[rt].R<=R){
if(M==3){
return K[rt].sum1;
}
else
return K[rt].mx1;
}
if(K[rt].mode){
trans(K[rt].mode,rt<<1);
trans(K[rt].mode,rt<<1|1);
modechan(rt<<1,rt);
modechan(rt<<1|1,rt);
K[rt].mode=0;
}
int m=K[rt].mid();
if(R<=m)
return query(M,L,R,rt<<1);
else if(L>m)
return query(M,L,R,rt<<1|1);
else{
int k1=query(M,L,R,rt<<1);
int k2=query(M,L,R,rt<<1|1);
if(M==4){
int s=min(K[rt<<1].rmax1,m-L+1)+min(K[rt<<1|1].lmax1,R-m);
return max(s,max(k1,k2));
}
else return k1+k2;
}
}
int main(){
int t,n,m;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&data[i]);
build(1,n,1);
int G,L,R;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&G,&L,&R); //这里我定义的区间是[1,n] 所以在给的数据上要+1
if(G==3||G==4)
printf("%d\n",query(G,L+1,R+1,1));
else
updata(G,L+1,R+1,1);
}
}
}