[BZOJ 1858] 序列操作(真·线段树大模板)
·题目描述
lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作:
0 a b 把[a,b]区间内的所有数全变成0
1 a b 把[a,b]区间内的所有数全变成1
2 a b 把[a,b]区间内的所有数全部取反,也就是说把所有的0变成1,把所有的1变成0
3 a b 询问[a,b]区间内总共有多少个1
4 a b 询问[a,b]区间内最多有多少个连续的1
对于每一种询问操作,lxhgww都需要给出回答,聪明的程序员们,你们能帮助他吗?(不能啊w(゚Д゚)w!!!)
·输入格式
输入数据第一行包括2个数,n和m,分别表示序列的长度和操作数目。
第二行包括n个数,表示序列的初始状态 接下来m行,每行3个数,op,a,b,(0≤op≤4,0≤a≤b)
·输出格式
对于每一个询问操作,输出一行,包括1个数,表示其对应的答案
·样例输入
10 10
0 0 0 1 1 0 1 0 1 1
1 0 2
3 0 5
2 2 2
4 0 4
0 3 6
2 3 7
4 2 8
1 0 5
0 5 6
3 3 9
·样例输出
5
2
6
5
·数据范围
对于30%的数据,1≤n,m≤1000
对于100%的数据,1≤n,m≤100000
题解:
不得不说,这道题真的是烦。一看见这道题就想到曾经数次打超长程序无限调试的经历Σ(っ °Д °;)っ。果不其然,这次的程序再一次刷新了我OI生涯中的最长程序纪录。
不多说了,这题就是对每个节点记录几个信息:\(LSum[x]\)为从左端点开始的最长的\(0\)或\(1\)个数,\(RSum[x]\)同理,而\(Sum[x]\)是该节点所代表的区间内\(0\)或\(1\)的和,\(len[x]\)就代表了这个区间内连续多少个\(0\)或\(1\)。
再考虑修改操作,对于操作\(3\),我们可以记录一个\(cover\)值,代表这个节点是否被\(0\)或\(1\)覆盖,然后在标记下传的时候进行维护。而对于操作\(4\),我们则是选择再次记录一个\(rev\)值,表示这个区间进行多少次翻转,翻转\(2\)次可以视为不翻转。
PS:我做这题+调试达到快5小时,我实在是蒟蒻啊。。。。。。
贴代码
#include <ctype.h>
#include <stdio.h>
#include <iostream>
#define Re register int
using namespace std;
struct SegmentTree{
int cover,rev,len[2],Sum[2];
int LSum[2],RSum[2];
}T[500005];
int N,M,A[100005],opt,a,b;
inline int read()
{
int x=0,w=0; char ch=0;
while (!isdigit(ch)) w|=ch=='-',ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return w?-x:x;
}
inline int max(int a,int b) {return a>b?a:b;}
inline int min(int a,int b) {return a<b?a:b;}
inline void swap(int &a,int &b) {a^=b; b^=a; a^=b;}
void PushUp(int id,int l,int r,int mid)
{
int ls=id*2,rs=id*2+1;
for (int i=0; i<2; ++i)
{
T[id].LSum[i]=(T[ls].LSum[i]==mid-l+1)?
T[ls].LSum[i]+T[rs].LSum[i]:T[ls].LSum[i];
T[id].RSum[i]=(T[rs].RSum[i]==r-mid)?
T[rs].RSum[i]+T[ls].RSum[i]:T[rs].RSum[i];
T[id].len[i]=max(T[ls].RSum[i]+T[rs].LSum[i],
max(T[ls].len[i],T[rs].len[i]));
T[id].Sum[i]=T[ls].Sum[i]+T[rs].Sum[i];
}
}
inline void PushDown(int id,int l,int r,int mid)
{
if (!T[id].rev&&T[id].cover==-1) return;
if (l!=r)
for (int son=id*2; son<=id*2+1; ++son)
{
if (T[id].cover!=-1)
{
T[son].cover=T[id].cover; T[son].rev=0; T[son].Sum[T[id].cover^1]=0;
T[son].Sum[T[id].cover]=(son==id*2)?mid-l+1:r-mid;
T[son].len[T[id].cover]=T[son].LSum[T[id].cover]=T[son].RSum[T[id].cover]=(son==id*2)?mid-l+1:r-mid;
T[son].len[T[id].cover^1]=T[son].LSum[T[id].cover^1]=T[son].RSum[T[id].cover^1]=0;
}
if (T[id].rev)
{
T[son].rev^=T[id].rev;
swap(T[son].len[0],T[son].len[1]);
swap(T[son].LSum[0],T[son].LSum[1]);
swap(T[son].RSum[0],T[son].RSum[1]);
swap(T[son].Sum[0],T[son].Sum[1]);
}
}
T[id].rev=0; T[id].cover=-1;
}
void Build(int id,int l,int r)
{
if (l==r)
{
T[id].cover=-1; T[id].rev=0; T[id].Sum[A[l]]=1;
T[id].len[A[l]]=1; T[id].LSum[A[l]]=T[id].RSum[A[l]]=1;
return;
}
int mid=(l+r)>>1; T[id].cover=-1;
Build(id*2,l,mid); Build(id*2+1,mid+1,r);
PushUp(id,l,r,mid);
}
void Modify(int id,int l,int r,int L,int R,int c)
{
if (L<=l&&r<=R)
{
T[id].cover=c; T[id].rev=0;
T[id].Sum[c]=r-l+1; T[id].Sum[c^1]=0;
T[id].len[c]=r-l+1; T[id].len[c^1]=0;
T[id].LSum[c]=r-l+1; T[id].LSum[c^1]=0;
T[id].RSum[c]=r-l+1; T[id].RSum[c^1]=0;
return;
}
int mid=(l+r)>>1; PushDown(id,l,r,mid);
if (L<=mid) Modify(id*2,l,mid,L,R,c);
if (mid<R) Modify(id*2+1,mid+1,r,L,R,c);
PushUp(id,l,r,mid);
}
void Reverse(int id,int l,int r,int L,int R)
{
if (L<=l&&r<=R)
{
T[id].rev^=1;
swap(T[id].Sum[0],T[id].Sum[1]);
swap(T[id].len[0],T[id].len[1]);
swap(T[id].LSum[0],T[id].LSum[1]);
swap(T[id].RSum[0],T[id].RSum[1]);
return;
}
int mid=(l+r)>>1; PushDown(id,l,r,mid);
if (L<=mid) Reverse(id*2,l,mid,L,R);
if (mid<R) Reverse(id*2+1,mid+1,r,L,R);
PushUp(id,l,r,mid);
}
int QuerySum(int id,int l,int r,int L,int R)
{
if (L<=l&&r<=R) return T[id].Sum[1];
int mid=(l+r)>>1,ret=0; PushDown(id,l,r,mid);
if (L<=mid) ret+=QuerySum(id*2,l,mid,L,R);
if (mid<R) ret+=QuerySum(id*2+1,mid+1,r,L,R);
return ret;
}
int QueryLen(int id,int l,int r,int L,int R)
{
if (L<=l&&r<=R) return T[id].len[1];
int mid=(l+r)>>1,ret=0; PushDown(id,l,r,mid);
if (L<=mid) ret=max(ret,QueryLen(id*2,l,mid,L,R));
if (mid<R) ret=max(ret,QueryLen(id*2+1,mid+1,r,L,R));
return max(ret,min(mid-L+1,T[id*2].RSum[1])+min(R-mid,T[id*2+1].LSum[1]));
}
int main(int argc, char const *argv[])
{
N=read(),M=read();
for (Re i=1; i<=N; ++i) A[i]=read();
Build(1,1,N);
for (Re i=1; i<=M; ++i)
{
opt=read(),a=read()+1,b=read()+1;
switch (opt)
{
case 0:case 1:Modify(1,1,N,a,b,opt); break;
case 2: Reverse(1,1,N,a,b); break;
case 3: printf("%d\n",QuerySum(1,1,N,a,b)); break;
case 4: printf("%d\n",QueryLen(1,1,N,a,b)); break;
}
}
return 0;
}