前言
P2572这道题真的相当折磨,相当难调了。希望这一篇题解可以帮到你。如果畏惧了超巨的码量请移步神仙压行。
本题解已尽最大努力肢解整一份代码,所以比较长。
线段树的构成
num[N],lazy[N]; //1的数量
num1[N];//连续1的数量
num0[N];//连续0的数量
lx[N],ly[N];//当前区间最左侧和最右侧的1/0连续数量(正数/负数)
lx,ly函数用于判断两个区间的1能否合并。正数表示连续1的数量,负数表示连续0的数量。
操作0~2的change函数
操作0
将整个区间直接赋值为0
。
那么区间1的数量和连续1的数量均变成0
区间连续0的数量为区间总长度。
当前最左、最右的0连续数量也为区间总长度,但是由于是0的连续数量所以值为负数。
num[id]=0;
num1[id]=0;
num0[id]=r-l+1;
lx[id]=ly[id]=-(r-l+1);//0是负数
lazy[id]=z; //lazy标记
操作1
将整个区间直接赋值为1
。
那么区间1的数量和连续1的数量均变成区间总长度
区间连续0的数量为0
。
当前最左、最右的1连续数量也为区间总长度,但是由于是1的连续数量所以值为正数。
num[id]=(r-l+1);
num1[id]=r-l+1;
num0[id]=0;
lx[id]=ly[id]=r-l+1;
lazy[id]=z;
操作2
将整个区间的数字取反。
那么0
和1
的数量将会调转。
所以区间1
的总数就是,区间0
的总数,即区间总长度减去1
的总数。
因为0
和1
调转,所以本来的连续0
变成了1
,1
变成0
。
num[id]=r-l+1-num[id];
swap(num1[id],num0[id]);//取反的话 最长连续1 和 最长连续0 正好调转
lx[id]*=-1;//取反的话 连续长度 但是正负要变
ly[id]*=-1;
关于操作2的lazy
在进行取反操作前如果有赋值操作那么要先进行赋值操作。
比如之前全赋值为1
取反之后相当于全赋值为0
。
进行取反的lazy标记时记得取反是可以叠加的。两次取反操作等于没操作。
if(lazy[id]==1)
{
lazy[id]=2;
}
else if(lazy[id]==2)
{
lazy[id]=1;
}
else
{
lazy[id]+=z;//取反状态不是可重复的是叠加的
}
查询3的query函数
简单的求和函数。
代码就不放了
查询4的ans函数
查询连续1
的数量时不能直接查询求max
。需要考虑拼接的情况。所以需要下面两个区间的lx
ly
的值。所以我们可以返回直接一个结构体。
下放函数pushdown
操作原理是和change函数一样的,不再赘述。
pushup函数
pushup函数关键是要考虑清楚两个区间如何合并。
我们举几个例子就好了。
比如合并区间[1,5]和[6,10]。
数字分别为10011
和10010
。
lx
,ly
分别为1,2
和1,-1
。
判断能否拼接只需要左半部分的最右侧(ly),和右半部分的最左侧(lx)是否同号即可。
那么大区间的lx
继承左半部分的lx
,ly
继承右半部分ly
。
当然会有一种特殊情况。如果某半边全是1
或者0
。那么lx
ly
不应仅是继承,应该是合并。
代码
#include<bits/stdc++.h>
#include<cstring>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#include<map>
#define ll long long
#define lhs printf("\n");
#define sync std::ios::sync_with_stdio(false),std::cin.tie(0),std::cout.tie(0);
using namespace std;
const int N=3e6+10;
const int M=2021;
const int inf=0x3f3f3f3f;
int n,m;
int a[N];
int num[N],lazy[N]; //1的数量
int num1[N];//连续1的数量
int num0[N];//连续0的数量
int lx[N],ly[N];//当前区间最左侧和最右侧的1/0连续数量(正数/负数)
struct node//用于查询连续1的数量时
{
int lx=0,ly=0,num=0;
};
void pushup(int id,int l,int r)
{
int mid=(l+r)/2;
num[id]=num[id*2]+num[id*2+1];
if(ly[id*2]>0 and lx[id*2+1]>0)//1可以拼起来
{
num1[id]=max(num1[id*2],max(num1[id*2+1],ly[id*2]+lx[id*2+1]));
}
else
{
num1[id]=max(num1[id*2],num1[id*2+1]);
}
if(ly[id*2]<0 and lx[id*2+1]<0)//0可以拼起来
{
num0[id]=max(num0[id*2],max(num0[id*2+1],-ly[id*2]-lx[id*2+1]));
}
else
{
num0[id]=max(num0[id*2],num0[id*2+1]);
}
if(lx[id*2]==mid-l+1 and lx[id*2+1]>0)//左下满了1 且 右下最左是1 那么拼在一起
{
lx[id]=lx[id*2]+lx[id*2+1];
}
else if(lx[id*2]==-(mid-l+1) and lx[id*2+1]<0)//左下满了0 且 右下最左是0 那么拼在一起
{
lx[id]=lx[id*2]+lx[id*2+1];
}
else
{
lx[id]=lx[id*2];
}
if(ly[id*2+1]==r-mid and ly[id*2]>0) //右下满了1 且 左下最有也是1
{
ly[id]=ly[id*2+1]+ly[id*2];
}
else if(ly[id*2+1]==-(r-mid) and ly[id*2]<0)//右下全是0 且 左下最右也是0
{
ly[id]=ly[id*2+1]+ly[id*2];
}
else
{
ly[id]=ly[id*2+1];
}
}
void pushdown(int id,int l,int r)
{
if(lazy[id])
{
int mid=(l+r)/2;
if(lazy[id]==1)//全变0
{
num[id*2]=0;
num0[id*2]=mid-l+1;
num1[id*2]=0;
lx[id*2]=ly[id*2]=-(mid-l+1);
lazy[id*2]=1;
num[id*2+1]=0;
num0[id*2+1]=r-mid;
num1[id*2+1]=0;
lx[id*2+1]=ly[id*2+1]=-(r-mid);
lazy[id*2+1]=1 ;
}
else if(lazy[id]==2)//全变1
{
num[id*2]=mid-l+1;
num0[id*2]=0;
num1[id*2]=mid-l+1;
lx[id*2]=ly[id*2]=(mid-l+1);
lazy[id*2]=2;
num[id*2+1]=r-mid;
num0[id*2+1]=0;
num1[id*2+1]=r-mid;
lx[id*2+1]=ly[id*2+1]=(r-mid);
lazy[id*2+1]=2;
}
else if(lazy[id]%3==0 and lazy[id]%2==1)//取反
{
num[id*2]=mid-l+1-num[id*2];
swap(num0[id*2],num1[id*2]);
lx[id*2]*=-1;
ly[id*2]*=-1;
if(lazy[id*2]==1)//需要先进行lazy的赋值下放操作
{
lazy[id*2]=2;
}
else if(lazy[id*2]==2)
{
lazy[id*2]=1;
}
else
{
lazy[id*2]+=3;
}
num[id*2+1]=r-mid-num[id*2+1];
swap(num0[id*2+1],num1[id*2+1]);
lx[id*2+1]*=-1;
ly[id*2+1]*=-1;
if(lazy[id*2+1]==1)//需要先进行lazy的赋值下放操作
{
lazy[id*2+1]=2;
}
else if(lazy[id*2+1]==2)
{
lazy[id*2+1]=1;
}
else
{
lazy[id*2+1]+=3;
}
}
lazy[id]=0;
}
}
void build(int id,int l,int r)
{
if(l==r)
{
num[id]=a[l];
if(a[l])//1
{
num1[id]=1;
lx[id]=ly[id]=1;
}
else//0
{
num0[id]=1;
lx[id]=ly[id]=-1;
}
return;
}
int mid=(l+r)/2;
build(id*2,l,mid);
build(id*2+1,mid+1,r);
pushup(id,l,r);
}
void change(int id,int l,int r,int x,int y,int z)
{
if(x<=l and r<=y)
{
if(z==1)//0
{
num[id]=0;
num1[id]=0;
num0[id]=r-l+1;
lx[id]=ly[id]=-(r-l+1);
lazy[id]=z;
}
else if(z==2)//1
{
num[id]=(r-l+1);
num1[id]=r-l+1;
num0[id]=0;
lx[id]=ly[id]=r-l+1;
lazy[id]=z;
}
else//取反
{
num[id]=r-l+1-num[id];
swap(num1[id],num0[id]);//取反的话 最长连续1 和 最长连续0 正好调转
lx[id]*=-1;//取反的话 连续长度 但是正负要变
ly[id]*=-1;
if(lazy[id]==1)
{
lazy[id]=2;
}
else if(lazy[id]==2)
{
lazy[id]=1;
}
else
{
lazy[id]+=z;//取反状态不是可重复的是叠加的
}
}
return;
}
pushdown(id,l,r);
int mid=(l+r)/2;
if(x<=mid)
{
change(id*2,l,mid,x,y,z);
}
if(mid+1<=y)
{
change(id*2+1,mid+1,r,x,y,z);
}
pushup(id,l,r);
}
int query(int id,int l,int r,int x,int y)//1数量
{
if(x<=l and r<=y)
{
return num[id];
}
pushdown(id,l,r);
int mid=(l+r)/2;
int sum=0;
if(x<=mid)
{
sum+=query(id*2,l,mid,x,y);
}
if(mid+1<=y)
{
sum+=query(id*2+1,mid+1,r,x,y);
}
return sum;
}
node ans(int id,int l,int r,int x,int y)//连续1数量
{
if(x<=l and r<=y)
{
int aa=lx[id],bb=ly[id],cc=num1[id];
node xx;
xx.lx=aa;
xx.ly=bb;
xx.num=cc;
return xx;
}
pushdown(id,l,r);
int mid=(l+r)/2;
int maxx=0;
node xx,yy;
int flag1=0,flag2=0;
if(x<=mid)
{
xx=ans(id*2,l,mid,x,y);
flag1=1;
}
if(mid+1<=y)
{
yy=ans(id*2+1,mid+1,r,x,y);
flag2=1;
}
int lxx,lyy;
if(x<=mid and mid+1<=y and xx.ly>0 and yy.lx>0) //1可以拼起来
{
if(xx.lx==mid-l+1 and yy.lx>0)//左下满了1 且 右下最左是1 那么拼在一起
{
lxx=xx.lx+yy.lx;
}
else
{
lxx=xx.lx;
}
if(yy.ly==r-mid and xx.ly>0) //右下满了1 且 左下最有也是1
{
lyy=xx.ly+yy.ly;
}
else
{
lyy=yy.ly;
}
int aa=lxx,bb=lyy,cc=max(xx.num,max(yy.num,xx.ly+yy.lx));
node s;
s.lx=aa;
s.ly=bb;
s.num=cc;
return s;
}
if(flag1 and flag2)//两边都访问到了那么取大的那边
{
int aa=xx.lx,bb=yy.ly,cc=max(xx.num,yy.num);
node s;
s.lx=aa;
s.ly=bb;
s.num=cc;
return s;
}
else//否则只取一边
{
if(flag1)
{
return xx;
}
else
{
return yy;
}
}
}
int main()
{
sync
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
build(1,1,n);
for(int i=1;i<=m;i++)
{
int op,x,y;
cin>>op>>x>>y;
x++;
y++;
if(op==0)
{
change(1,1,n,x,y,op+1);
}
else if(op==1)
{
change(1,1,n,x,y,op+1);
}
else if(op==2)
{
change(1,1,n,x,y,op+1);
}
else if(op==3)
{
cout<<query(1,1,n,x,y)<<"\n";
}
else
{
cout<<ans(1,1,n,x,y).num<<"\n";
}
}
return 0;
}
/*
10 10
1 0 0 0 1 0 1 1 0 1
0 6 8
1 0 8
2 1 7
2 6 6
4 6 8
1 6 6
4 6 7
0 0 6
1 5 7
4 9 9
答案:1 1 1
*/