【洛谷P5500】[LnOI2019]真正的OIer从不女装
线段树
题目大意:
给定一个序列,支持两个操作:
- 给定 $l,r,x$,将 $a_l\sim a_r$ 都变为 $x$。
- 给定 $l,r,k$,每次可以选择一个整数 $p\in[l,r)$ 并翻转区间 $[l,p]$ 和 $[p+1,r]$ 内的数,最多操作 $k$ 次,要求最大化所有值相等的子区间的长度。求最大长度。操作完后区间会复原到操作之前的状态。
题解:
考虑那个翻转的操作。
事实上,当 $k>1$ 的时候,能得到的最大答案和 $k=1$ 的情况相同。
如果一次翻转产生了贡献,那么操作前,区间最左边和最右边都应该是目标数。然后一次翻转使得两个目标数所在区间合并,产生额外贡献。
但是,翻转一次后,区间两边的数就变为了原来分裂位置的两个数,而这两个数我们是不要的。再翻转一次,无论位置在哪里,合并到一起的一定还是这两个数,无法产生更多的贡献。
所以只需要考虑不操作和操作一次的情况即可。
这个就可以用线段树维护区间信息了。每个区间维护三个值 $pre,suf,mx$,分别表示前缀相同区间长度、后缀相同区间长度和区间内最大相同子区间长度。同时记录 $pre,suf,mx$ 分别对应的数值,转移时判断是否能合并。
合并的时候可以根据两段区间的这三个值,$O(1)$ 完成求出合并后的区间的这三个值。
查询时,不操作的就是 $mx$ 的值,操作一次使,如果两边的数相同且整个区间 $[l,r]$ 不止一种值,则合并出来的更大的贡献就是 $pre+suf$。
区间赋值操作直接打标记即可。
时间复杂度 $O((n+m)\log n)$。
Code:
#include<iostream>
#include<utility>
#include<algorithm>
using namespace std;
const int N=2e5+5;
int n,m;
int tag[N<<2];
pair<int,int>pre[N<<2],suf[N<<2],mx[N<<2];
struct data{
pair<int,int>pre,suf,mx;
int len;
};
inline data merge(data x,data y){
data ret;
ret.pre=x.pre.first==x.len&&x.pre.second==y.pre.second?make_pair(x.len+y.pre.first,x.pre.second):x.pre;
ret.suf=y.suf.first==y.len&&y.suf.second==x.suf.second?make_pair(y.len+x.suf.first,y.suf.second):y.suf;
ret.mx=max(x.mx,y.mx);
if(x.suf.second==y.pre.second&&x.suf.first+y.pre.first>ret.mx.first)
ret.mx=make_pair(x.suf.first+y.pre.first,x.suf.second);
ret.len=x.len+y.len;
return ret;
}
inline void pushdown(int o,int len){
pre[o<<1]=suf[o<<1]=mx[o<<1]=make_pair(len+1>>1,tag[o]);
pre[o<<1|1]=suf[o<<1|1]=mx[o<<1|1]=make_pair(len>>1,tag[o]);
tag[o<<1]=tag[o<<1|1]=tag[o];
tag[o]=0;
}
inline void update(int o,int len){
pre[o]=(pre[o<<1].first==(len+1>>1)&&pre[o<<1].second==pre[o<<1|1].second)?
make_pair((len+1>>1)+pre[o<<1|1].first,pre[o<<1].second):pre[o<<1];
suf[o]=(suf[o<<1|1].first==(len>>1)&&suf[o<<1].second==suf[o<<1|1].second)?
make_pair((len>>1)+suf[o<<1].first,suf[o<<1|1].second):suf[o<<1|1];
mx[o]=max(mx[o<<1],mx[o<<1|1]);
if(suf[o<<1].second==pre[o<<1|1].second&&suf[o<<1].first+pre[o<<1|1].first>mx[o].first)
mx[o]=make_pair(suf[o<<1].first+pre[o<<1|1].first,suf[o<<1].second);
}
void modify(int l,int r,int o,const int&L,const int&R,const int&v){
if(L<=l&&r<=R){
pre[o]=suf[o]=mx[o]=make_pair(r-l+1,tag[o]=v);
return;
}
if(tag[o])pushdown(o,r-l+1);
const int mid=l+r>>1;
if(L<=mid)modify(l,mid,o<<1,L,R,v);
if(mid<R)modify(mid+1,r,o<<1|1,L,R,v);
update(o,r-l+1);
}
data query(int l,int r,int o,const int&L,const int&R){
if(L<=l&&r<=R)return(data){pre[o],suf[o],mx[o],r-l+1};
if(tag[o])pushdown(o,r-l+1);
const int mid=l+r>>1;
if(L<=mid&&mid<R)return merge(query(l,mid,o<<1,L,R),query(mid+1,r,o<<1|1,L,R));
if(L<=mid)return query(l,mid,o<<1,L,R);return query(mid+1,r,o<<1|1,L,R);
}
void build(int l,int r,int o){
if(l==r){
int x;
cin>>x;
pre[o]=suf[o]=mx[o]=make_pair(1,x);
return;
}
const int mid=l+r>>1;
build(l,mid,o<<1),build(mid+1,r,o<<1|1);
update(o,r-l+1);
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
build(1,n,1);
while(m--){
char op[3];
int l,r,x;
cin>>op>>l>>r>>x;
if(*op=='R')
modify(1,n,1,l,r,x);
else{
data g=query(1,n,1,l,r);
int ans=g.mx.first;
if(x&&g.pre.second==g.suf.second&&g.pre.first<r-l+1)ans=max(ans,g.pre.first+g.suf.first);
cout<<ans<<'\n';
}
}
return 0;
}