题解 真正的Oier从不女装

P5500 真正的Oier从不女装

众所周知,女装只有0次和无数次!

题目看这里
题目要求我们维护一个序列,支持区间赋值和查询,每次查询进行 \(k\) 次女装操作之后,序列中最长连续段(数字相同视为连续)的长度。
对于一次女装操作,定义为从给定区间 \([l,r]\) 中挑选一个点 \(p\),分别对 \([l,p]\)\([p+1,r]\) 进行反转。
我们考虑女装操作对我们的序列造成的影响:假设我们的序列为 \(A={1,2,3,4,5,6}\)。我们选定断点为 3,则一次女装之后,序列变为\(A={3,2,1,6,5,4}\)。我们知道,把序列倒过来不会影响最长连续段的长度,所以我们把它倒过来看,就变成了 \(A={4,5,6,1,2,3}\),于是我们惊奇的发现,实际上一次女装操作就相当于把序列的前若干个数截下来放到了序列的末尾。知道了女装操作的本质,我们思考:对于 \(k\) 次女装操作,实际上是重复从序列前端截取了 \(k\) 次并挪到后面。实际上我们只是把这 \(k\) 次截取到的序列的总长度模掉 \(n\) 这么一小段序列挪到了末尾,而这个操作显然可以通过一次操作搞定。所以女装任意次等价于女装一次。也就是说,只要女装次数上限不为 1,就需要把查询的区间 \([l,r]\) 中维护的区间最长连续段长度和区间左界开始的最长连续段长度与区间右界结束的最长连续段长度的和取较大值作为答案,女装真的只有0次和无数次!
所以现在只需要考虑如何维护。我们需要维护一颗线段树,支持区间修改,线段树内维护区间最长连续段长度 \(mxn\),包含区间左端点的最长连续段长度 \(mxl\) 和这一段的颜色 \(cl\),包含区间右端点的最长连续段长度 \(mxr\) 和这一段的颜色 \(cr\),以及区间长度 \(len\),还有区间赋值的标记 \(tag\)
大部分都是正常步骤,需要特别处理的只有 \(pushup\)\(query\)
\(pushup\) 函数
在这个函数中,对于区间合并,先把大区间的 \(mxn\) 设为左右区间的 \(mxn\) 的较大值,把大区间的 \(mxl\) 设为左区间的 \(mxl\),把大区间的 \(mxr\) 设为右区间的 \(mxr\),然后进行如下三个特判:

  1. 如果左区间的 \(mxl\) 覆盖了整个左区间并且左区间的颜色和右区间的 \(mxl\) 的颜色相同,就合并左右区间的 \(mxl\)
  2. 如果右区间的 \(mxr\) 覆盖了整个右区间并且右区间的颜色和左区间的 \(mxr\) 的颜色相同,就合并左右区间的 \(mxr\)
  3. 如果左区间的 \(cr\) 和右区间的 \(cl\) 相同,说明左区间的 \(mxr\) 和右区间的 \(mxl\) 会合并成一个更大的区间,将这个区间的长度和当前大区间的 \(mxn\) 取较大值。

\(query\) 函数
在主函数得到答案时,我们需要知道查询的区间的 \(mxn,mxl,mxr,cl,cr\) 五个变量,所以我们在查询时需要维护这些变量。所以我们直接把线段树的结构体 \(segTree\) 作为 \(query\) 函数的返回值类型。每次查询时,如果左右区间都需要递归查询,分别查询以后按照 \(pushup\) 的逻辑进行合并。注意合并时要判越界,详细原因间代码。如果只需要递归左或右区间,直接递归即可。
注意查询得到的 \(mxl,mxr\) 可能有重合,所以统计答案时,如果允许女装,需要把 \(mxl+mxr\) 和区间长度取较小值,再和 \(mxn\) 比较。
代码如下:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define N 500010
#define ls(p) p<<1
#define rs(p) p<<1|1
#define mxn(p) t[p].mxn
#define tag(p) t[p].tag
#define mxl(p) t[p].mxl
#define mxr(p) t[p].mxr
#define cl(p) t[p].cl
#define cr(p) t[p].cr
#define len(p) t[p].len

int n,m,x,y,k;
char op;
int a[N];
struct segTree{
	int mxn,mxl,cl,mxr,cr,tag,len;
	segTree(){
		cl=cr=mxn=mxl=mxr=len=0;
	}
}t[4*N];

/*线段树*/
void push_up(int p){
	len(p)=len(ls(p))+len(rs(p));
	mxn(p)=max(mxn(ls(p)),mxn(rs(p)));
	mxl(p)=mxl(ls(p));
	cl(p)=cl(ls(p));
	mxr(p)=mxr(rs(p));
	cr(p)=cr(rs(p));
	if(mxl(ls(p))==len(ls(p))&&cl(ls(p))==cl(rs(p)))
		mxl(p)=mxl(ls(p))+mxl(rs(p));
	if(mxr(rs(p))==len(rs(p))&&cr(ls(p))==cl(rs(p)))
		mxr(p)=mxr(ls(p))+mxr(rs(p));
	if(cr(ls(p))==cl(rs(p))){
		mxn(p)=max(mxn(p),mxr(ls(p))+mxl(rs(p)));
	}
	return;
}

void push_down(int p,int l,int r){
	if(tag(p)==0) return;
	int mid=(l+r)>>1;
	mxn(ls(p))=mxl(ls(p))=mxr(ls(p))=mid-l+1;
	mxn(rs(p))=mxl(rs(p))=mxr(rs(p))=r-mid;
	cl(ls(p))=cl(rs(p))=cr(ls(p))=cr(rs(p))=tag(p);
	tag(ls(p))=tag(rs(p))=tag(p);
	tag(p)=0;
	return;
}

void build(int p,int l,int r){
	tag(p)=0;
	if(l==r){
		cl(p)=cr(p)=a[l];
		mxn(p)=mxl(p)=mxr(p)=len(p)=1;
		return;
	}
	int mid=(l+r)>>1;
	build(ls(p),l,mid);
	build(rs(p),mid+1,r);
	push_up(p);
	return;
} 

void modify(int p,int l,int r,int L,int R,int k){
	if(L<=l&&r<=R){
		mxn(p)=mxl(p)=mxr(p)=len(p)=r-l+1;
		cl(p)=cr(p)=k;
		tag(p)=k;
		return;
	}
	push_down(p,l,r);
	int mid=(l+r)>>1;
	if(L<=mid) modify(ls(p),l,mid,L,R,k);
	if(R>mid) modify(rs(p),mid+1,r,L,R,k);
	push_up(p);
	return;
}

segTree query(int p,int l,int r,int L,int R){
	if(L<=l&&r<=R) return t[p];
	push_down(p,l,r);
	segTree res=segTree();
	int mid=(l+r)>>1;
	if(L<=mid&&mid<R){
		segTree ll=query(ls(p),l,mid,L,R);
		segTree rr=query(rs(p),mid+1,r,L,R);
		res.len=ll.len+rr.len;
		res.mxn=max(rr.mxn,ll.mxn);
		res.cl=ll.cl; res.mxl=ll.mxl;
		res.cr=rr.cr; res.mxr=rr.mxr;
		if(ll.mxl==ll.len&&ll.cr==rr.cl) res.mxl=ll.mxl+rr.mxl;
		if(rr.mxr==rr.len&&rr.cl==ll.cr) res.mxr=ll.mxr+rr.mxr;
		if(ll.cr==rr.cl)
		//注意这里查询区间不一定完全覆盖节点区间,需要判越界 
			res.mxn=max(res.mxn,min(ll.mxr,mid-L+1)+min(rr.mxl,R-mid));
	}
	else if(L<=mid) res=query(ls(p),l,mid,L,R);
	else if(R>mid) res=query(rs(p),mid+1,r,L,R);
	return res;
}

signed main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	build(1,1,n);
	for(int i=1;i<=m;i++){
		cin>>op>>x>>y>>k;
		if(op=='R'){
			modify(1,1,n,x,y,k);
		}else{
			segTree tmp=query(1,1,n,x,y);
			int ans=tmp.mxn;
			if(k&&tmp.cl==tmp.cr) 
				ans=max(ans,min(tmp.mxl+tmp.mxr,y-x+1));
			cout<<ans<<"\n";
		}
	}
	return 0;
}

posted @ 2025-05-29 17:33  Yun_Mo_s5_013  阅读(22)  评论(0)    收藏  举报