P2572 [SCOI2010] 序列操作
一道线段树好 (史) 题,细节很多。
说实话这个东西真的很大杂烩。主要还是难在标记与标记的合并以及信息与信息间的合并上。
由于有区间翻转操作,又得找连续的 1 区间,所以我们对于 0 和 1 都要维护一个类似最大子段和的东西:区间出现次数,最长前/后缀长度,最长连续长度,以及一个是否被赋值成当前数的懒标记。
然后分别考虑每个操作:
-
翻转操作。这个最好说了,直接将 0 和 1 维护的东西全部交换过来,并将对应的 \(tag\) 加一。
-
区间赋值操作:先将对应的 \(tag\) 变成 1,将另一个数的 \(tag\) 重置,随后将当前数的区间出现次数,最长前/后缀长度,最长连续长度全部赋值为区间长度,将另一个数的这些量全部置为 0。
-
查询当前 1 的个数:线段树模版区间加和查询的操作。
-
查询当前连续的 1 的个数:非常类似最大子段和,我们类比最大子段和输出即可。
为什么我们一直在说“类似最大子段和”呢?因为它和最大子段和很像,都是维护 \(sum,lsum,rsum,duan\) 4 个信息。但是我们维护的是连续的 1/0 的个数,所以我们 pushup 维护 \(lsum,rsum\) 的时候要判断另一个区间是不是全部为 1/0。
这么说好像有点抽象。。。比如我们现在在维护 \(id\) 结点中 1 的 \(lsum\),左子树是 \(ls\),右子树为 \(rs\)。那么当我们试图用 \(tr[ls].sum+tr[rs].lsum\) 更新 \(tr[id].lsum\) 时,需要判断 \(ls\) 的区间是否是全 1 的。
思路上并不难,接下来我们看代码:
P2572
#include<bits/stdc++.h>
#define int long long
#define ls (id<<1)
#define rs ((id<<1)|1)
using namespace std;
inline int read(){
int x=0,f=1;char c=getchar();
while(c<48){
if(c=='-') f=-1;
c=getchar();
}
while(c>47) x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
const int N=1e5+5;
int n,m,a[N];
struct sw{
int id,l,r;
int sum[2],lsum[2],rsum[2],duan[2];
int tag[2],tag2;
}tr[N<<2];
inline sw pushup(sw p,sw q){
sw qwq;
for(int i=0;i<2;i++){
qwq.sum[i]=p.sum[i]+q.sum[i];
qwq.lsum[i]=p.lsum[i];
if(p.sum[i]==(p.r-p.l+1)){
//这种情况只能从左边全i转移来
qwq.lsum[i]=max(qwq.lsum[i],p.sum[i]+q.lsum[i]);
}
qwq.rsum[i]=q.rsum[i];
if(q.sum[i]==(q.r-q.l+1)){
//这种情况只能从右边全i转移来
qwq.rsum[i]=max(qwq.rsum[i],q.sum[i]+p.rsum[i]);
}
//同最大子段和
qwq.duan[i]=max(max(p.duan[i],q.duan[i]),p.rsum[i]+q.lsum[i]);
}
return qwq;
}
inline void REV(int id){
//将 id 里的01翻转
//显然只需要让有关01的量交换
swap(tr[id].sum[0],tr[id].sum[1]);
swap(tr[id].lsum[0],tr[id].lsum[1]);
swap(tr[id].rsum[0],tr[id].rsum[1]);
swap(tr[id].duan[0],tr[id].duan[1]);
tr[id].tag2++;
}
inline void TUI(int id,int k){
tr[id].tag[k]=1;tr[id].tag[k^1]=-1;
//注意细节问题
tr[id].tag2=0;
int len=tr[id].r-tr[id].l+1;
tr[id].sum[k]=len;tr[id].sum[k^1]=0;
tr[id].lsum[k]=len;tr[id].lsum[k^1]=0;
tr[id].rsum[k]=len;tr[id].rsum[k^1]=0;
tr[id].duan[k]=len;tr[id].duan[k^1]=0;
}
inline void pushdown(int id){
if(tr[id].tag[0]!=-1){
TUI(ls,0);TUI(rs,0);
tr[id].tag[0]=-1;
}
if(tr[id].tag[1]!=-1){
TUI(ls,1);TUI(rs,1);
tr[id].tag[1]=-1;
}
if(tr[id].tag2&1){
REV(ls);REV(rs);
tr[id].tag2=0;
}
}
inline void build(int id,int l,int r){
tr[id].l=l,tr[id].r=r;
tr[id].tag[0]=tr[id].tag[1]=-1;tr[id].tag2=0;
if(l==r){
//初始化
if(a[l]==0){
tr[id].sum[0]=1,tr[id].sum[1]=0;
tr[id].lsum[0]=1,tr[id].lsum[1]=0;
tr[id].rsum[0]=1,tr[id].rsum[1]=0;
tr[id].duan[0]=1,tr[id].duan[1]=0;
}
else{
tr[id].sum[0]=0,tr[id].sum[1]=1;
tr[id].lsum[0]=0,tr[id].lsum[1]=1;
tr[id].rsum[0]=0,tr[id].rsum[1]=1;
tr[id].duan[0]=0,tr[id].duan[1]=1;
}
return ;
}
int mid=(l+r)>>1;
build(ls,l,mid);build(rs,mid+1,r);
sw qwq=pushup(tr[ls],tr[rs]);
tr[id].sum[0]=qwq.sum[0],tr[id].sum[1]=qwq.sum[1];
tr[id].lsum[0]=qwq.lsum[0],tr[id].lsum[1]=qwq.lsum[1];
tr[id].rsum[0]=qwq.rsum[0],tr[id].rsum[1]=qwq.rsum[1];
tr[id].duan[0]=qwq.duan[0],tr[id].duan[1]=qwq.duan[1];
}
inline void update(int id,int l,int r,int k,int op){
if(l<=tr[id].l&&tr[id].r<=r){
if(op==2){
REV(id);
}
else{
TUI(id,k);
}
return ;
}
pushdown(id);
int mid=(tr[id].l+tr[id].r)>>1;
if(l<=mid) update(ls,l,r,k,op);
if(r>mid) update(rs,l,r,k,op);
sw qwq=pushup(tr[ls],tr[rs]);
tr[id].sum[0]=qwq.sum[0],tr[id].sum[1]=qwq.sum[1];
tr[id].lsum[0]=qwq.lsum[0],tr[id].lsum[1]=qwq.lsum[1];
tr[id].rsum[0]=qwq.rsum[0],tr[id].rsum[1]=qwq.rsum[1];
tr[id].duan[0]=qwq.duan[0],tr[id].duan[1]=qwq.duan[1];
}
inline int query1(int id,int l,int r){
if(l<=tr[id].l&&tr[id].r<=r){
return tr[id].sum[1];
}
pushdown(id);
int mid=(tr[id].l+tr[id].r)>>1,ans=0;
if(l<=mid) ans+=query1(ls,l,r);
if(r>mid) ans+=query1(rs,l,r);
return ans;
}
inline sw query2(int id,int l,int r){
if(l<=tr[id].l&&tr[id].r<=r){
return tr[id];
}
pushdown(id);
int mid=(tr[id].l+tr[id].r)>>1;
//以下代码更便于实现(大概
if(l>mid) return query2(rs,l,r);
else if(r>mid) return pushup(query2(ls,l,r),query2(rs,l,r));
else return query2(ls,l,r);
}
signed main(){
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 op=read(),l=read(),r=read();
//原题 a 数组下标从 0 开始
l++;r++;
//全部赋值为 0
if(op==0) update(1,l,r,0,0);
//全部赋值为 1
if(op==1) update(1,l,r,1,1);
//区间反转
if(op==2) update(1,l,r,2,2);
if(op==3){//1的个数
int ans=query1(1,l,r);
printf("%lld\n",ans);
}
if(op==4){//最长的连续 1
sw qwq=query2(1,l,r);
int ans=qwq.duan[1];
printf("%lld\n",ans);
}
}
return 0;
}

浙公网安备 33010602011771号