链接
题意
有5种操作
- op = 0 ,改变[a,b]区间值全为0
- op = 1 ,改变[a,b]区间值全为1
- op = 2 ,改变[a,b]区间值翻转,0 --> 1 ,1 --> 0
- op = 3,,查询[a,b]区间所有1的个数
- op =4,查询[a,b]区间最长连续1的个数
思路
首先很明显需要用线段树来维护区间信息,
线段树维护的信息
int sum[man<<2];//维护区间和
int flag1[man<<2],flag2[man<<2];
//置0,1 flag1 flag1 = 1,置为1, flag1 = 2置为0 ,翻转flag2
int Rlen1[man<<2],Llen1[man<<2],mlen1[man<<2];
//区间右边开始最长连续1,左边开始最长连续1,中间最长连续1
int Rlen0[man<<2],Llen0[man<<2],mlen0[man<<2];
//区间右边开始最长连续0,左边开始最长连续0,中间最长连续0
说明一下为什么需要维护这些信息:sum求区间和实现操作3
Rlen1,Llen1,mlen1实现操作4,Rlen0,Llen0,mlen0,用于操作2翻转,flag1,falg2延迟标记
01翻转就可以用Llen1与Rlen0交换
对于更新
pushup(从下往上更新答案)
inline void pushup(int rt,int l,int r){
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
int m = l + r >> 1;
if(sum[rt<<1] == m - l + 1)Llen1[rt] = Llen1[rt<<1] + Llen1[rt<<1|1];
//表示左边区间全为1,这个大区间(rt区间)左边连续1就得在加上右区间从左开始连续1的个数
else Llen1[rt] = Llen1[rt<<1];//否则只为左区间从左开始连续1的个数
if(sum[rt<<1|1] == r - m)Rlen1[rt] = Rlen1[rt<<1|1] + Rlen1[rt<<1];
//表示右边区间全为1,这个大区间(rt区间)右边连续1就得在加上左区间从右开始连续1的个数
else Rlen1[rt] = Rlen1[rt<<1|1];//否则只为左区间从左开始连续1的个数
mlen1[rt] = Llen1[rt<<1|1] + Rlen1[rt<<1];//中间部分有左区间右连续+右区间左连续
mlen1[rt] = max(mlen1[rt],max(mlen1[rt<<1],mlen1[rt<<1|1]));//在于左右区间的中间取max
//下面同理
if(sum[rt<<1] == 0)Llen0[rt] = Llen0[rt<<1] + Llen0[rt<<1|1];
else Llen0[rt] = Llen0[rt<<1];
if(sum[rt<<1|1] == 0)Rlen0[rt] = Rlen0[rt<<1|1] + Rlen0[rt<<1];
else Rlen0[rt] = Rlen0[rt<<1|1];
mlen0[rt] = Llen0[rt<<1|1] + Rlen0[rt<<1];
mlen0[rt] = max(mlen0[rt],max(mlen0[rt<<1],mlen0[rt<<1|1]));
}
pushdown(下推懒惰标记)
inline void pushdown(int rt,int l,int r){
if(l==r)return;
int m = l + r >> 1;
//因为全置操作比xor操作要高,所以要先进行
if(flag1[rt]){//全置为1 or 0
flag1[rt<<1] = flag1[rt<<1|1] = flag1[rt];//下推
flag2[rt<<1|1] = flag2[rt<<1] = 0;//这里必须得把xor的标记给清掉,因为覆盖掉了
//flag2[rt] = 0不能清掉现在的标记,如果有的话那么一定是在置0,1后面进行的
//因为我每次全置0,1,我都是把之前的flag2标记置为0了,如果这之后还有,那一定是在后面
//得把这里置为0之后在翻转。
int tp = flag1[rt]==1 ? 1 : 0;
sum[rt<<1] = (m - l + 1)*tp;
sum[rt<<1|1] = (r - m)*tp;
Llen1[rt<<1] = Rlen1[rt<<1] = mlen1[rt<<1] = sum[rt<<1];
Llen1[rt<<1|1] = Rlen1[rt<<1|1] = mlen1[rt<<1|1] = sum[rt<<1|1];
Llen0[rt<<1] = Rlen0[rt<<1] = mlen0[rt<<1] = m - l + 1 - sum[rt<<1];
Llen0[rt<<1|1] = Rlen0[rt<<1|1] = mlen0[rt<<1|1] = r - m - sum[rt<<1|1];
flag1[rt] = 0;
}
if(flag2[rt]){//翻转
flag2[rt<<1] ^= 1;
flag2[rt<<1|1] ^= 1;
//0和1翻转,相当于个数交换
swap(Llen0[rt<<1],Llen1[rt<<1]);
swap(Rlen0[rt<<1],Rlen1[rt<<1]);
swap(mlen0[rt<<1],mlen1[rt<<1]);
swap(Llen0[rt<<1|1],Llen1[rt<<1|1]);
swap(Rlen0[rt<<1|1],Rlen1[rt<<1|1]);
swap(mlen0[rt<<1|1],mlen1[rt<<1|1]);
sum[rt<<1] = m - l + 1 - sum[rt<<1];
sum[rt<<1|1] = r - m - sum[rt<<1|1];
flag2[rt] = 0;
}
}
update更新操作
inline void update(int l,int r,int L,int R,int rt,int op){
if(L<=l&&r<=R){
if(1==op||2==op){
flag1[rt] = op;
flag2[rt] = 0;//记住这里也得清零
int tp = op==1 ? 1 : 0;
sum[rt] = tp*(r-l+1);
Rlen1[rt] = Llen1[rt] = mlen1[rt] = sum[rt];
Rlen0[rt] = Llen0[rt] = mlen0[rt] = r - l + 1 - sum[rt];
}else{
flag2[rt] ^= 1;//这里是翻转
//这里其实不用下推全置0,1操作,因为每次都是先下推全置0,1,不会有影响
sum[rt] = r - l + 1 - sum[rt];
swap(Llen0[rt],Llen1[rt]);
swap(Rlen0[rt],Rlen1[rt]);
swap(mlen0[rt],mlen1[rt]);
}
return;
}
int m = l + r >> 1;
pushdown(rt,l,r);
if(L<=m)update(l,m,L,R,rt<<1,op);
if(R>m)update(m+1,r,L,R,rt<<1|1,op);
pushup(rt,l,r);
}
查询
inline int query1(int l,int r,int L,int R,int rt){
if(L<=l&&r<=R){
return sum[rt];
}
int m = l + r >>1;
pushdown(rt,l,r);
int ans = 0;
if(L<=m)ans += query1(l,m,L,R,rt<<1);
if(R>m)ans += query1(m+1,r,L,R,rt<<1|1);
return ans;
}
inline int query2(int l,int r,int L,int R,int rt){
if(L<=l&&r<=R){
return max(max(Llen1[rt],Rlen1[rt]),mlen1[rt]);
}
int m = l + r >>1;
pushdown(rt,l,r);
int ans = -INT_MAX;
// if(L<=m)ans = max(ans,query2(l,m,L,R,rt<<1)); //结束条件 L <= l && r <= R
// if(R>m)ans = max(ans,query2(m+1,r,L,R,rt<<1|1));
// return max(ans,min(Rlen1[rt<<1],m-L+1)+min(Llen1[rt<<1|1],R-m));
if(L>m)ans = query2(m+1,r,L,R,rt<<1|1);
else if(R<=m)ans = query2(l,m,L,R,rt<<1);
else{
//ans = max(query2(l,m,L,m,rt<<1),query2(m+1,r,m+1,R,rt<<1|1));// 结束条件 L == l && r == R
ans = max(query2(l,m,L,R,rt<<1),query2(m+1,r,L,R,rt<<1|1));
int tp = min(Rlen1[rt<<1],m-L+1) + min(Llen1[rt<<1|1],R-m);
ans = max(ans,tp);
}
return ans;
}
完整代码
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int man = 2e5+10;
typedef long long ll;
const ll mod = 1e9+7;
int a[man];
int sum[man<<2];//维护区间和
int flag1[man<<2],flag2[man<<2];
//置0,1 flag1 flag1 = 1,置为1, flag1 = 2置为0 ,翻转flag2
int Rlen1[man<<2],Llen1[man<<2],mlen1[man<<2];
//区间右边开始最长连续1,左边开始最长连续1,中间最长连续1
int Rlen0[man<<2],Llen0[man<<2],mlen0[man<<2];
//区间右边开始最长连续0,左边开始最长连续0,中间最长连续0
inline void pushup(int rt,int l,int r){
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
int m = l + r >> 1;
if(sum[rt<<1] == m - l + 1)Llen1[rt] = Llen1[rt<<1] + Llen1[rt<<1|1];
//表示左边区间全为1,这个大区间(rt区间)左边连续1就得在加上右区间从左开始连续1的个数
else Llen1[rt] = Llen1[rt<<1];//否则只为左区间从左开始连续1的个数
if(sum[rt<<1|1] == r - m)Rlen1[rt] = Rlen1[rt<<1|1] + Rlen1[rt<<1];
//表示右边区间全为1,这个大区间(rt区间)右边连续1就得在加上左区间从右开始连续1的个数
else Rlen1[rt] = Rlen1[rt<<1|1];//否则只为左区间从左开始连续1的个数
mlen1[rt] = Llen1[rt<<1|1] + Rlen1[rt<<1];//中间部分有左区间右连续+右区间左连续
mlen1[rt] = max(mlen1[rt],max(mlen1[rt<<1],mlen1[rt<<1|1]));//在于左右区间的中间取max
//下面同理
if(sum[rt<<1] == 0)Llen0[rt] = Llen0[rt<<1] + Llen0[rt<<1|1];
else Llen0[rt] = Llen0[rt<<1];
if(sum[rt<<1|1] == 0)Rlen0[rt] = Rlen0[rt<<1|1] + Rlen0[rt<<1];
else Rlen0[rt] = Rlen0[rt<<1|1];
mlen0[rt] = Llen0[rt<<1|1] + Rlen0[rt<<1];
mlen0[rt] = max(mlen0[rt],max(mlen0[rt<<1],mlen0[rt<<1|1]));
}
inline void pushdown(int rt,int l,int r){
if(l==r)return;
int m = l + r >> 1;
//因为全置操作比xor操作要高,所以要先进行
if(flag1[rt]){//全置为1 or 0
flag1[rt<<1] = flag1[rt<<1|1] = flag1[rt];//下推
flag2[rt<<1|1] = flag2[rt<<1] = 0;//这里必须得把xor的标记给清掉,因为覆盖掉了
//flag2[rt] = 0不能清掉现在的标记,如果有的话那么一定是在置0,1后面进行的
//因为我每次全置0,1,我都是把之前的flag2标记置为0了,如果这之后还有,那一定是在后面
//得把这里置为0之后在翻转。
int tp = flag1[rt]==1 ? 1 : 0;
sum[rt<<1] = (m - l + 1)*tp;
sum[rt<<1|1] = (r - m)*tp;
Llen1[rt<<1] = Rlen1[rt<<1] = mlen1[rt<<1] = sum[rt<<1];
Llen1[rt<<1|1] = Rlen1[rt<<1|1] = mlen1[rt<<1|1] = sum[rt<<1|1];
Llen0[rt<<1] = Rlen0[rt<<1] = mlen0[rt<<1] = m - l + 1 - sum[rt<<1];
Llen0[rt<<1|1] = Rlen0[rt<<1|1] = mlen0[rt<<1|1] = r - m - sum[rt<<1|1];
flag1[rt] = 0;
}
if(flag2[rt]){//翻转
flag2[rt<<1] ^= 1;
flag2[rt<<1|1] ^= 1;
//0和1翻转,相当于个数交换
swap(Llen0[rt<<1],Llen1[rt<<1]);
swap(Rlen0[rt<<1],Rlen1[rt<<1]);
swap(mlen0[rt<<1],mlen1[rt<<1]);
swap(Llen0[rt<<1|1],Llen1[rt<<1|1]);
swap(Rlen0[rt<<1|1],Rlen1[rt<<1|1]);
swap(mlen0[rt<<1|1],mlen1[rt<<1|1]);
sum[rt<<1] = m - l + 1 - sum[rt<<1];
sum[rt<<1|1] = r - m - sum[rt<<1|1];
flag2[rt] = 0;
}
}
inline void build(int l,int r,int rt){
flag1[rt] = flag2[rt] = 0;
if(l==r){
sum[rt] = a[l];
Rlen1[rt] = Llen1[rt] = mlen1[rt] = sum[rt];
Rlen0[rt] = Llen0[rt] = mlen0[rt] = 1 - sum[rt];
return;
}
int m = l + r >> 1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
pushup(rt,l,r);
}
inline void update(int l,int r,int L,int R,int rt,int op){
if(L<=l&&r<=R){
if(1==op||2==op){
flag1[rt] = op;
flag2[rt] = 0;//记住这里也得清零
int tp = op==1 ? 1 : 0;
sum[rt] = tp*(r-l+1);
Rlen1[rt] = Llen1[rt] = mlen1[rt] = sum[rt];
Rlen0[rt] = Llen0[rt] = mlen0[rt] = r - l + 1 - sum[rt];
}else{
flag2[rt] ^= 1;//这里是翻转
//这里其实不用下推全置0,1操作,因为每次都是先下推全置0,1,不会有影响
sum[rt] = r - l + 1 - sum[rt];
swap(Llen0[rt],Llen1[rt]);
swap(Rlen0[rt],Rlen1[rt]);
swap(mlen0[rt],mlen1[rt]);
}
return;
}
int m = l + r >> 1;
pushdown(rt,l,r);
if(L<=m)update(l,m,L,R,rt<<1,op);
if(R>m)update(m+1,r,L,R,rt<<1|1,op);
pushup(rt,l,r);
}
inline int query1(int l,int r,int L,int R,int rt){
if(L<=l&&r<=R){
return sum[rt];
}
int m = l + r >>1;
pushdown(rt,l,r);
int ans = 0;
if(L<=m)ans += query1(l,m,L,R,rt<<1);
if(R>m)ans += query1(m+1,r,L,R,rt<<1|1);
return ans;
}
inline int query2(int l,int r,int L,int R,int rt){
if(L<=l&&r<=R){
return max(max(Llen1[rt],Rlen1[rt]),mlen1[rt]);
}
int m = l + r >>1;
pushdown(rt,l,r);
int ans = -INT_MAX;
// if(L<=m)ans = max(ans,query2(l,m,L,R,rt<<1)); //结束条件 L <= l && r <= R
// if(R>m)ans = max(ans,query2(m+1,r,L,R,rt<<1|1));
// return max(ans,min(Rlen1[rt<<1],m-L+1)+min(Llen1[rt<<1|1],R-m));
if(L>m)ans = query2(m+1,r,L,R,rt<<1|1);
else if(R<=m)ans = query2(l,m,L,R,rt<<1);
else{
//ans = max(query2(l,m,L,m,rt<<1),query2(m+1,r,m+1,R,rt<<1|1));// 结束条件 L == l && r == R
ans = max(query2(l,m,L,R,rt<<1),query2(m+1,r,L,R,rt<<1|1));
int tp = min(Rlen1[rt<<1],m-L+1) + min(Llen1[rt<<1|1],R-m);
ans = max(ans,tp);
}
return ans;
}
inline void print(int l,int r,int rt){
// if(l==r){
printf("l:%d r:%d ",l,r);
printf("rt:%d sum:%d llen1:%d rlen1:%d mlen1:%d ",rt,sum[rt],Llen1[rt] , Rlen1[rt] , mlen1[rt]);
printf("llen0:%d rlen0:%d mlen0:%d ",Llen0[rt] , Rlen0[rt] , mlen0[rt]);
printf("flag1:%d flag2:%d\n",flag1[rt],flag2[rt]);
// return;
// }
if(l==r)return;
int m = l + r >> 1;
print(l,m,rt<<1);
print(m+1,r,rt<<1|1);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
//freopen("out.txt","w",stdout);
#endif
int t;
scanf("%d",&t);
while(t--){
int n,m;
//memset(flag1,-1,sizeof(flag1));
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++){
scanf("%d",&a[i]);
}
build(1,n,1);
//print(1,n,1);
while(m--){
int op,a,b;
scanf("%d%d%d",&op,&a,&b);
a++,b++;
if(0==op){
update(1,n,a,b,1,2);
}else if(1==op){
update(1,n,a,b,1,1);
}else if(2==op){
update(1,n,a,b,1,3);
}else if(3==op){
printf("%d\n",query1(1,n,a,b,1));
}else{
printf("%d\n",query2(1,n,a,b,1));
}
//print(1,n,1);cout << endl;
}
}
return 0;
}