P5692 [MtOI2019] 手牵手走向明天
题意#
将区间内所有
题解#
是第四分块:P5397 [Ynoi2018] 天降之物的改版。去掉了强制在线,操作和询问从全局加强到了区间,所以不能用根号分治做法了。
考虑对序列分块,记块长为
那么考虑维护块内的所有答案,首先我们在每个块内离散化,将值域映射到
那么显然答案的计算就是记录当前的
再来考虑修改,讨论一下:
-
散块:暴力扫,修改,然后
暴力重构此块中 的信息。此部分总复杂度为 。 -
整块:
-
块内不存在
:直接忽略,不进行操作; -
块内存在
但不存在 :直接修改离散化值,把 赋给 ,然后将 置为 。此部分总复杂度为 。 -
块内
都存在:同散块处理,暴力扫,修改,然后 暴力重构此块中 的信息。因为在一个块内每进行一次此操作都会使块内不同数值的个数恰好减少一个,所以一个块内最多进行 次此操作,总共最多进行 次。所以此部分总复杂度为 。
-
总时间复杂度为
用逐块处理的 trick 可以做到线性空间复杂度。
实现上有一个容易被忽略的细节:
我们在进行散块修改的时候因为可能只修改了块内的一部分 ++idx
的话,RE
,如果直接开大数组,空间复杂度会达到 就会爆炸。
正确的解决方法是,我们用一个 vector
存下当前“空闲”的 vector
,之后我们就可以用 vector
里面存的“空闲”值,如果 vector
为空我们才 ++idx
。这样可以保证
#
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define il inline
#define re register
#define getchar()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<22,stdin),p1==p2)?EOF:*p1++)
char buf[1<<22],*p1=buf,*p2=buf;
const int N=1e5+10,sq=360,len=350,star=1e8,V=1e5;
int n,m,a[N],L,R,cntB,idx,l,r,x,y;
int fst[sq],lst[sq],ans[sq][sq],id[N];
vector<int>v;
#define pb emplace_back
il int read(){
re int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*f;
}
struct query{
int op,l,r,x,y,ans;
int lstx,lsty;
}q[N];
il void Calc(int &i){
for(re int j=1;j<=idx;j++)ans[i][j]=star;
ans[i][i]=0,lst[i]=0;
for(re int j=L,now=star;j<=R;j++)
if(a[j]==i)now=0,lst[i]=j;
else ans[i][a[j]]=ans[a[j]][i]=min(ans[i][a[j]],++now);
for(re int j=R,now=star;j>=L;j--)
if(a[j]==i)now=0,fst[i]=j;
else ans[i][a[j]]=ans[a[j]][i]=min(ans[i][a[j]],++now);
if(!lst[i])v.pb(i),i=0;
}
il void Build(){
vector<int>().swap(v);
for(re int i=1;i<=V;i++)id[i]=0;
idx=0;
for(re int i=L;i<=R;i++){
if(!id[a[i]])id[a[i]]=++idx;
a[i]=id[a[i]];
}
for(re int i=1;i<=idx;i++)Calc(i);
}
il void Modify(int &x,int &y){
if(!x||x==y)return;
if(l>L||r<R){
if(!y){
if(v.empty())y=++idx;
else y=v.back(),v.pop_back();
}
for(re int i=max(l,L);i<=min(r,R);i++)if(a[i]==x)a[i]=y;
return Calc(x),Calc(y);
}
if(!y)return y=x,x=0,void();
for(re int i=L;i<=R;i++)if(a[i]==x)a[i]=y;
return Calc(x),Calc(y),void();
}
il void Solve(int &res,int &lstx,int &lsty){
x=id[x],y=id[y];
if(!x&&!y)return;
if(l>L||r<R){
for(re int i=max(l,L);i<=min(r,R);i++){
if(a[i]==x&&a[i]==y)return res=0,void();
else if(a[i]==x)res=min(res,i-lsty),lstx=i;
else if(a[i]==y)res=min(res,i-lstx),lsty=i;
}
return;
}
if(x==y)return res=0,void();
if(x)res=min(res,fst[x]-lsty);
if(y)res=min(res,fst[y]-lstx);
if(x)lstx=lst[x];
if(y)lsty=lst[y];
if(x&&y)res=min(res,ans[x][y]);
}
int main(){
n=read(),m=read(),cntB=(n-1)/len+1;
for(re int i=1;i<=n;i++)a[i]=read();
for(re int i=1;i<=m;i++)
q[i].op=read(),q[i].l=read(),q[i].r=read(),
q[i].x=read(),q[i].y=read(),q[i].ans=star,
q[i].lstx=q[i].lsty=-star;
for(re int _=1;_<=cntB;_++){
L=R+1,R=min(_*len,n);
Build();
for(re int i=1;i<=m;i++){
l=q[i].l,r=q[i].r,x=q[i].x,y=q[i].y;
if(l>R||r<L)continue;
if(q[i].op==1)Modify(id[x],id[y]);
else Solve(q[i].ans,q[i].lstx,q[i].lsty);
}
}
for(re int i=1;i<=m;i++)
if(q[i].op==2)cout<<((q[i].ans==star)?-1:q[i].ans)<<'\n';
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)