P2572 [SCOI2010] 序列操作 题解
解题思路
其实还是很好想的,但是似乎有一点难写大概率是我写史了。
直接无脑分块。
我们将序列分为若干块后,直接每一个操作分别模拟即可:
- 区间修改: 我们对于整块直接给块赋一个统一的值就行了,对于零散块和在一个块内的则暴力修改;
- 区间反转: 我们对于整块打上一个反转标记,每次异或
,对于零散块和在一个块内的则暴力修改; - 查询块内
的个数: 对于零散块和在一个块内的暴力统计,对于整块我们则对于每一个块维护一下 的数量和 的数量,反转的时候交换一下就可以了; - 查询最长连续
的个数: 同样,我们对于零散块和在一个块内的直接暴力 计算即可。对于整块,我们维护这个块分别以块的左右端点为端点的连续 、 长度以及块内最长 、 长度。查询的时候我们则考虑相邻块的影响:前一个块的以右端点为端点的连续段和当前块以左端点为端点的连续段可以拼接为一个新的段。那么我们可以维护一个前面若干个段拼接后的段的长度,和当前段加起来和答案取 即可。
代码还是很好写的,直接按照上面的步骤维护
AC 代码
代码不长,但是写的时候一定要思路清晰……
#include<math.h>
#include<stdio.h>
#include<valarray>
#include<stdlib.h>
#include<algorithm>
#define N 100005
#define M 350
int n,q,a[N],pos[N];
struct Block{
//块的左右端点
int l,r;
//块从左端点开始最长连续0、1的长度
int MaxL0,MaxL1;
//块从右端点开始最长连续0、1的长度
int MaxR0,MaxR1;
//块内最长连续0、1的长度
int Max0,Max1;
//维护整个块的值和块长
int val,len;
//标记这个块是否反转
bool Rev;
//块内0和1的个数
int Cnt0,Cnt1;
//封装函数维护整块操作
//维护前4个操作即可
//初始化块
Block(){
l=r=val=len=0;
MaxL0=MaxL1=0;
MaxR0=MaxR1=0;
Cnt0=Cnt1=0;
Max0=Max1=0;
Rev=false;
}
//初始化块值
inline void Init(){
Rev=false;
Cnt0=Cnt1=0;
for(int i=l;i<=r;++i){
if(a[i]&1)
++Cnt1;
else ++Cnt0;
}
len=r-l+1;val=-1;
Max0=Max1=0;
MaxL0=MaxL1=l;
MaxR0=MaxR1=r;
//求出从左端点开始最长0、1连续段
while(a[MaxL0]==0&&MaxL0<=r)
++MaxL0;--MaxL0;
while(a[MaxL1]==1&&MaxL1<=r)
++MaxL1;--MaxL1;
//求出从右端点开始最长0、1连续段
while(a[MaxR0]==0&&MaxR0>=l)
--MaxR0;++MaxR0;
while(a[MaxR1]==1&&MaxR1>=l)
--MaxR1;++MaxR1;
MaxL0=MaxL0-l+1;
MaxL1=MaxL1-l+1;
MaxR0=r-MaxR0+1;
MaxR1=r-MaxR1+1;
//求出块内最长0、1连续段长度
int now0=0,now1=0;
for(int i=l;i<=r;++i){
if(a[i]!=0&&a[i-1]==0)
if(now0>Max0)
Max0=now0;
if(a[i]==0&&a[i-1]!=0)
now0=0;
if(a[i]==0) ++now0;
}
if(now0>Max0) Max0=now0;
for(int i=l;i<=r;++i){
if(a[i]!=1&&a[i-1]==1)
if(now1>Max1)
Max1=now1;
if(a[i]==1&&a[i-1]!=1)
now1=0;
if(a[i]==1) ++now1;
}
if(now1>Max1) Max1=now1;
}
//区间赋值
inline void Assignment(
const int &v
){
val=v;
//如果是1
if(v&1){
//更新0、1个数
Cnt0=0,Cnt1=len;
//更新最长值
Max0=MaxL0=MaxR0=0;
Max1=MaxL1=MaxR1=len;
}else{
//更新0、1个数
Cnt1=0,Cnt0=len;
//更新最长值
Max1=MaxL1=MaxR1=0;
Max0=MaxL0=MaxR0=len;
}
//取消区间反转
Rev=false;
}
//区间反转
inline void Rollback(){
//交换0、1个数
std::swap(Cnt0,Cnt1);
//交换块内最长值
std::swap(Max0,Max1);
//交换左端点最长值
std::swap(MaxL0,MaxL1);
//交换右端点最长值
std::swap(MaxR0,MaxR1);
//标记反转,反转两次等于不反转
Rev^=1;
}
//查询区间1的个数
inline int QueryCnt(){
return Cnt1;
}
}block[M];
inline void InitBlock(){
scanf("%d%d",&n,&q);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
int t=sqrt(n);
for(int i=1;i<=t;++i){
block[i].l=(i-1)*t+1;
block[i].r=i*t;
}
if(block[t].r<n){
++t; block[t].r=n;
block[t].l=block[t-1].r+1;
}
for(int i=1;i<=t;++i)
block[i].Init();
for(int i=1;i<=t;++i)
for(
int j=block[i].l;
j<=block[i].r;++j
) pos[j]=i;
}
//区间修改
inline void Assignment(
const int &l,
const int &r,
const int &v
){
int x=pos[l];
int y=pos[r];
if(x==y){
int rev=block[x].Rev;
int L=block[x].l;
int R=block[x].r;
if(block[x].val!=-1){
for(int i=L;i<=R;++i)
a[i]=block[x].val;
}
for(int i=L;i<=R;++i)
a[i]^=rev;
for(int i=l;i<=r;++i)
a[i]=v;
block[x].Init();
block[x].val=-1;
return;
}
int rev=block[x].Rev;
int L=block[x].l;
int R=block[x].r;
if(block[x].val!=-1){
for(int i=L;i<=R;++i)
a[i]=block[x].val;
}
for(int i=L;i<=R;++i)
a[i]^=rev;
for(int i=l;i<=block[x].r;++i)
a[i]=v;
block[x].Init();
block[x].val=-1;
rev=block[y].Rev;
L=block[y].l;
R=block[y].r;
if(block[y].val!=-1){
for(int i=L;i<=R;++i)
a[i]=block[y].val;
}
for(int i=L;i<=R;++i)
a[i]^=rev;
for(int i=block[y].l;i<=r;++i)
a[i]=v;
block[y].Init();
block[y].val=-1;
for(int i=x+1;i<y;++i)
block[i].Assignment(v);
}
//区间反转
inline void Rollback(
const int &l,
const int &r
){
int x=pos[l];
int y=pos[r];
if(x==y){
int rev=block[x].Rev;
int L=block[x].l;
int R=block[x].r;
if(block[x].val!=-1){
for(int i=L;i<=R;++i)
a[i]=block[x].val;
}
for(int i=L;i<=R;++i)
a[i]^=rev;
for(int i=l;i<=r;++i)
a[i]^=1;
block[x].Init();
block[x].val=-1;
return;
}
int rev=block[x].Rev;
int L=block[x].l;
int R=block[x].r;
if(block[x].val!=-1){
for(int i=L;i<=R;++i)
a[i]=block[x].val;
}
for(int i=L;i<=R;++i)
a[i]^=rev;
for(int i=l;i<=block[x].r;++i)
a[i]^=1;
block[x].Init();
block[x].val=-1;
rev=block[y].Rev;
L=block[y].l;
R=block[y].r;
if(block[y].val!=-1){
for(int i=L;i<=R;++i)
a[i]=block[y].val;
}
for(int i=L;i<=R;++i)
a[i]^=rev;
for(int i=block[y].l;i<=r;++i)
a[i]^=1;
block[y].Init();
block[y].val=-1;
for(int i=x+1;i<y;++i)
block[i].Rollback();
}
//区间查询1的数量
inline void QueryCnt(
const int &l,
const int &r
){
int x=pos[l];
int y=pos[r];
if(x==y){
int rev=block[x].Rev;
int L=block[x].l;
int R=block[x].r;
if(block[x].val!=-1){
for(int i=L;i<=R;++i)
a[i]=block[x].val;
}
for(int i=L;i<=R;++i)
a[i]^=rev;
block[x].Init();
block[x].val=-1;
int res=0;
for(int i=l;i<=r;++i)
res+=a[i];
printf("%d\n",res);
return;
}
int res=0;
int rev=block[x].Rev;
int L=block[x].l;
int R=block[x].r;
if(block[x].val!=-1){
for(int i=L;i<=R;++i)
a[i]=block[x].val;
}
for(int i=L;i<=R;++i)
a[i]^=rev;
for(int i=l;i<=block[x].r;++i)
res+=a[i];
block[x].Init();
block[x].val=-1;
rev=block[y].Rev;
L=block[y].l;
R=block[y].r;
if(block[y].val!=-1){
for(int i=L;i<=R;++i)
a[i]=block[y].val;
}
for(int i=L;i<=R;++i)
a[i]^=rev;
for(int i=block[y].l;i<=r;++i)
res+=a[i];
block[y].Init();
block[y].val=-1;
for(int i=x+1;i<y;++i)
res+=block[i].QueryCnt();
printf("%d\n",res);
}
//区间查询最长1连续段
inline void QueryMax(
const int &l,
const int &r
){
int x=pos[l];
int y=pos[r];
if(x==y){
int rev=block[x].Rev;
int L=block[x].l;
int R=block[x].r;
if(block[x].val!=-1){
for(int i=L;i<=R;++i)
a[i]=block[x].val;
}
for(int i=L;i<=R;++i)
a[i]^=rev;
block[x].Init();
block[x].val=-1;
int now=0,res=0;
for(int i=l;i<=r;++i){
if(a[i]!=1&&a[i-1]==1)
if(now>res)
res=now;
if(a[i]==1&&a[i-1]!=1)
now=0;
if(a[i]==1) ++now;
}
if(now>res) res=now;
printf("%d\n",res);
return;
}
int rev=block[x].Rev;
int L=block[x].l;
int R=block[x].r;
if(block[x].val!=-1){
for(int i=L;i<=R;++i)
a[i]=block[x].val;
}
for(int i=L;i<=R;++i)
a[i]^=rev;
block[x].Init();
block[x].val=-1;
int now=0,res=0;
for(int i=l;i<=block[x].r;++i){
if(a[i]!=1&&a[i-1]==1)
if(now>res)
res=now;
if(a[i]==1&&a[i-1]!=1)
now=0;
if(a[i]==1) ++now;
}
if(now>res) res=now;
rev=block[y].Rev;
L=block[y].l;
R=block[y].r;
if(block[y].val!=-1){
for(int i=L;i<=R;++i)
a[i]=block[y].val;
}
for(int i=L;i<=R;++i)
a[i]^=rev;
block[y].Init();
block[y].val=-1;
now=0;
for(int i=block[y].l;i<=r;++i){
if(a[i]!=1&&a[i-1]==1)
if(now>res)
res=now;
if(a[i]==1&&a[i-1]!=1)
now=0;
if(a[i]==1) ++now;
}
if(now>res) res=now;
int xr=0,yl=0;
for(int i=block[x].r;i>=l;--i){
if(a[i]!=1) break;
++xr;
}
for(int i=block[y].l;i<=r;++i){
if(a[i]!=1) break;
++yl;
}
int lst=xr;
for(int i=x+1;i<y;++i){
int nowl=block[i].MaxL1;
int nowr=block[i].MaxR1;
if(lst+nowl>res)
res=lst+nowl;
if(nowl==block[i].len)
lst+=nowl;
else lst=nowr;
if(nowr>res)
res=nowr;
if(block[i].Max1>res)
res=block[i].Max1;
}
if(lst+yl>res)
res=lst+yl;
printf("%d\n",res);
}
signed main(){
InitBlock();
int opt,l,r;
while(q--){
scanf("%d",&opt);
scanf("%d%d",&l,&r);
++l,++r;if(l>r)
std::swap(l,r);
switch (opt){
case 0:Assignment(l,r,0);break;
case 1:Assignment(l,r,1);break;
case 2:Rollback(l,r);break;
case 3:QueryCnt(l,r);break;
case 4:QueryMax(l,r);break;
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下