题解 P5397【[Ynoi2018] 天降之物】
这是一篇分块题解!
题意
给你一个长为 序列的 ,支持操作共 次:
- 将所有等于 的数修改为 ,即 ,执行 。
- 查询序列中 与 出现位置两两之差的最小值,即求 。
时间 500ms,.
思路
前置知识:P4119 [Ynoi2018] 未来日记 - 第一分块。
第四分块。
大家都知道标算是根号分治,但我不会!
于是我们使用序列分块!
(说实话还是我第一道自己写出来的大分块,虽然难度不大)
先忽略修改操作,考虑查询分为块内与块间两部分贡献。
块间贡献统计只与每个块内第一次与最后一次出现 的位置有关,所以维护每个数在每一块中第一次和最后一次的出现位置,分别记为 ,其中 是块, 是数。可知这里空间是 的。
考虑块内贡献直接维护每两个数之间在该块的答案显然会爆空间。在第一分块我们知道,由于块内的数只会有 种,可以对块内进行离散化,即对每个块 中出现过的数 ,记录 ,满足 且 。同时,记录其逆 使得 。这时再记录 表示 与 在块中的答案。可知这里空间也是 的。
查询时块间与块内分开算。块内就是每个有 且有 的块 中, 的最小值。块间则扫每个块,记录上个 的出现位置,用 计算答案, 更新即可。
现在加上了修改操作。
首先我们知道要对每个块考虑。
第一分块中,我们也见过这个玩意。具体地,分类讨论:
- 块中无 :直接跳过;
- 块中有 无 :令 ;
- 块中有 有 :令每个块的势能为块中不同数的个数,初始时,每个块的势能是 的,每次进行此类修改都会将势能减一,所以我们只需将每次此类修改的时间控制在 即可。显然地,令 即可。
至此,修改部分做到了 。于是我们在时空均为 的复杂度内完成了此题。
但是,由于常数较大,我不经任何常数处理的代码只能得到 分。
于是我们需要优化(重要的我会标粗):
- 对于空间常数的优化 :
我们可以知道, 与 的值都是在 以内的。考虑用 short
存下它们。
但是 与 的值是 的,不能用 short
存(我知道可以将其转化至 ,但其对时间常数不利),于是用 代替 ,便可将这部分空间优化至 。
其实我们不需要专门开 表示块 中是否有 ,用 判断即可。
- 选择合适的块长:典中典,此时块长选 可得 分。最终卡常时我的块长为 。
- 三角形存储 :即只存储 ,此时便可得到 分。
- 适当降低预处理常数:如减少耗时 的循环,此时便可得到 分。
- 数组的顺序 :用 代替 ,访问连续了许多,此时可得 分。
- 适当降低重构常数:减少循环即可,此时可得 分。
- 重构分段循环 :由于记录了 ,我们在重构时将循环按照与 和 的关系划分为三段,而不是一段并实时判断大小关系,此时可得 分。
- 对查询的特判:特判 的情况便可通过点 ,得到 分,但我也不知道为什么。
- 压缩存储方式 :若将后两位压缩需用到大量乘法操作,耗费时间多。考虑压缩前两维,考虑到第二维小于块长,则令 表示 ,其中 为块 的左端点减一。
- 预处理常数再削减 :不要在 的循环中使用判断,预先对所有有用的部分进行
memset
,只需循环取 。此时可得 分。
使用了上述卡常方式后,仅需微调块长即可通过。
提一句,通过离线逐块处理(这里块间询问平凡,也可逐块处理)可将空间复杂度降至 ,但是有强制在线。
代码非常好写,以下代码仅供参考:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff
}
const int N=1e5+10,sq=255,nq=N/sq+10;
int n,q,blk,last,a[N],b[N],bl[nq],br[nq],bel[N],tp[nq];
int fir[N],sec[N],ind[N];
short inp[N][nq],ins[N][sq];
inline int site(int x,int y){
if(x>y) swap(x,y);
return x*sq+y;
}
inline void init(int l,int r){
int d=bel[l],p=l-1;
vector<int>vec;
for(int i=l;i<=r;i++)
vec.push_back(a[i]);
sort(vec.begin(),vec.end());
vec.resize(unique(vec.begin(),vec.end())-vec.begin());
tp[d]=vec.size();
for(int i=l;i<=r;i++)
b[i]=lower_bound(vec.begin(),vec.end(),a[i])-vec.begin()+1,
ind[p+b[i]]=a[i],inp[a[i]][d]=b[i];
for(int i=l;i<=r;i++)
sec[p+b[i]]=i;
for(int i=r;i>=l;i--)
fir[p+b[i]]=i;
for(int i=1;i<=tp[d];i++)
memset(ins[p+i],64,tp[d]+1<<1);
for(int i=l;i<=r;i++)
for(int j=i+1;j<=r;j++){
int A=min(b[i],b[j]),B=max(b[i],b[j]);
ins[p+A][B]=min(ins[p+A][B],(short)(j-i));
}
}
inline void rebuild(int l,int r,int x,int y){
int d=bel[l],p=l-1;
int inpx=inp[x][d],inpy=inp[y][d];
fir[p+inpy]=min(fir[p+inpy],fir[p+inpx]),sec[p+inpy]=max(sec[p+inpy],sec[p+inpx]);
fir[p+inpx]=sec[p+inpx]=0,inp[x][d]=0;
if(inpx>inpy){
for(int i=1;i<=inpy;i++)
ins[p+i][inpy]=min(ins[p+i][inpy],ins[p+i][inpx]);
for(int i=inpy+1;i<=inpx;i++)
ins[p+inpy][i]=min(ins[p+inpy][i],ins[p+i][inpx]);
for(int i=inpx+1;i<=tp[d];i++)
ins[p+inpy][i]=min(ins[p+inpy][i],ins[p+inpx][i]);
}else{
for(int i=1;i<=inpx;i++)
ins[p+i][inpy]=min(ins[p+i][inpy],ins[p+i][inpx]);
for(int i=inpx+1;i<=inpy;i++)
ins[p+i][inpy]=min(ins[p+i][inpy],ins[p+inpx][i]);
for(int i=inpy;i<=tp[d];i++)
ins[p+inpy][i]=min(ins[p+inpy][i],ins[p+inpx][i]);
}
}
int main(){
n=read(),q=read();
for(int i=1;i<=n;i++)
a[i]=read();
for(int i=1;i<=n;i+=sq){
blk++;
bl[blk]=i,br[blk]=min(i+sq-1,n);
for(int j=bl[blk];j<=br[blk];j++)
bel[j]=blk;
init(bl[blk],br[blk]);
}
while(q--){
int opt=read(),x=read()^last,y=read()^last;
if(opt==2){
int ans=n+1,lasx=-1e9,lasy=-1e9;
if(x==y){
for(int i=1;i<=blk;i++)
if(inp[x][i]) ans=0;
if(ans==n+1) puts("Ikaros"),last=0;
else printf("%d\n",last=ans);
continue;
}
for(int i=1;i<=blk;i++){
int inpx=0,inpy=0,p=bl[i]-1;
if(inpx=inp[x][i]) ans=min(ans,fir[p+inpx]-lasy);
if(inpy=inp[y][i]) ans=min(ans,fir[p+inpy]-lasx);
if(inpx&&inpy) ans=min(ans,(int)ins[p+min(inpx,inpy)][max(inpx,inpy)]);
if(inpx) lasx=sec[p+inpx];
if(inpy) lasy=sec[p+inpy];
}
if(ans==n+1) puts("Ikaros"),last=0;
else printf("%d\n",last=ans);
}else{
if(x==y) continue;
for(int i=1;i<=blk;i++){
int t=inp[x][i],p=bl[i]-1;
if(!t) continue;
if(!inp[y][i])
ind[p+t]=y,inp[x][i]=0,inp[y][i]=t;
else
rebuild(bl[i],br[i],x,y);
}
}
// last=0;
}
flush();
}
再见 qwq~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话