P5500 [LnOI2019] 真正的 OIer 从不女装

P5500 [LnOI2019] 真正的 OIer 从不女装

题意简述 :

维护一个序列,支持两种操作:

1.区间覆盖。
2.在区间 [l,r] 上女装不超过 k后区间最长相同连续子序列。

其中女装操作是指对于 [l,r] 任选一个 p[l,r] ,将[l,p](p,r] 分别翻转。

Solution:

首先我们思考一下如果没有这个略显怪异的女装操作的话我们该怎么做这题:  

很简单的线段树对吧,维护几个值 lmx,rmx,mx 表示必须选左\右端点和无限制时区间的最大答案。

我们再来看一下女装操作到底在干什么:
假设 q=p+1 (如果存在的话)。
假设原区间长这样:
l....pq....r
女装过后: 
p....lr....q
将区间整个翻转显然不会影响答案:  
q....rl....p

到这里我们不难发现:女装的实质就是将一段序列 [q,r] 从序列尾删除然后重新拼到序列的前面,那么显然这样的操作进行多次是无意义的,我们只需要进行使得答案最大的那一次女装就好了。

然后我们再来思考一下女装会对答案产生什么样的影响:将这个序列断开又反拼之后,[l,p] , [q,r] 两个区间内的局部答案是不收影响的,然而对于 [l,r] 我们多了一种可以取 [q,r].rmx+[l,p].lmx 的可能。

前面已经说过真正有用的女装操作其实就一次而已,所以对于最终的答案,我们女装之后对答案可能的最大贡献即为:

[l,r].rmx+[l,r].lmx

但还要注意的是,因为 [l,r].rmx,[l,r].lmx 有可能有重叠部分,所以我们的答案还要对 rl+1min

然后这题就愉快的做完了。

Code:

#include<bits/stdc++.h>
const int N=2e5+5;
using namespace std;
int col[N];
//Segment_Tree
struct Tree{
int lmx,rmx,mx,l,r,tag;
int col[2];
};
struct Segment_Tree{
#define ls x<<1
#define rs x<<1|1
Tree t[N<<2];
void pushup(Tree &T,Tree L,Tree R)
{
int l=L.l,r=R.r,mid1=L.r,mid2=R.l;
T.lmx= L.lmx + (l+L.lmx==mid2&&L.col[1]==R.col[0] ? R.lmx : 0);
T.rmx= R.rmx + (r-R.rmx==mid1&&L.col[1]==R.col[0] ? L.rmx : 0);
T.mx=max({T.lmx,T.rmx,L.mx,R.mx,(L.col[1]==R.col[0] ? L.rmx+R.lmx : 0)});
T.l=l,T.r=r;
T.col[0]=L.col[0],T.col[1]=R.col[1];
return ;
}
void change(Tree &T,int k)
{
int len=T.r-T.l+1;
T=Tree{len,len,len,T.l,T.r,k,k,k};
}
void pushdown(int x)
{
if(!t[x].tag)return ;
int tag=t[x].tag;t[x].tag=0;
change(t[ls],tag);change(t[rs],tag);
}
void build(int x,int l,int r)
{
if(l==r)
{
t[x]=Tree{1,1,1,l,r,0,col[l],col[r]};
return ;
}
int mid=l+r>>1;
build(ls,l,mid);build(rs,mid+1,r);
pushup(t[x],t[ls],t[rs]);
}
void upd(int x,int L,int R,int k)
{
if(L<=t[x].l&&t[x].r<=R)
{
change(t[x],k);
return;
}
pushdown(x);
int mid=t[x].l+t[x].r>>1;
if(L<=mid)upd(ls,L,R,k);
if(mid<R) upd(rs,L,R,k);
pushup(t[x],t[ls],t[rs]);
}
void query(int x,int L,int R,Tree &res)
{
if(L<=t[x].l&&t[x].r<=R)
{
if(!res.l)res=t[x];
else pushup(res,res,t[x]);
return;
}
int mid=t[x].l+t[x].r>>1;
pushdown(x);
if(L<=mid)query(ls,L,R,res);
if(mid<R) query(rs,L,R,res);
}
#undef ls
#undef rs
}T;
char c[N];
int n,m;
void work()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
scanf("%d",&col[i]);
}
T.build(1,1,n);
for(int i=1,l,r,k;i<=m;i++)
{
scanf("%s",c);
scanf("%d%d%d",&l,&r,&k);
if(c[0]=='R')
{
T.upd(1,l,r,k);
}
if(c[0]=='Q')
{
int ans=0;
Tree res={0,0,0,0,0,0,0,0};
T.query(1,l,r,res);
if(k)ans=max(res.mx,min(r-l+1,(res.col[0]==res.col[1] ? res.lmx+res.rmx : 0)));
else ans=res.mx;
printf("%d\n",ans);
}
}
}
int main()
{
//freopen("P5500.in","r",stdin);freopen("P5500.out","w",stdout);
work();
return 0;
}
posted @   liuboom  阅读(2)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示