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;
}
posted @ 2025-11-10 19:52  qwqSW  阅读(3)  评论(0)    收藏  举报