[Ynoi2018] そらのおとしもの
「弑尽破净的第四分块」
Link
Prob
伊卡洛斯给了你一个长为 \(n\) 的序列 \(a\)。
你需要实现 \(m\) 个操作,操作有两种:
- 把序列中所有值为 \(x\) 的数的值变成 \(y\)。
- 找出一个位置 \(i\) 满足 \(a_i=x\),找出一个位置 \(j\) 满足 \(a_j=y\),使得 \(|i-j|\) 最小,并输出 \(|i-j|\)。
对于 \(100\%\) 的数据,所有数在 \([1,10^5]\) 内,每次操作的值不超过 \(n\)。
Time : 500 ms
Memory : 256 MiB
LXL Diff : 6
Solution
考虑序列分块,每一块里面维护某个值的第一个出现位置,某一个值的最后一个出现位置,和两个值之间在块内最小距离。
对块内值进行离散化可以把空间复杂度降到 \(O(nB)\)。
考虑块内如何修改:
-
\(x\) 不存在:跳过。
-
\(y\) 不存在:将 \(x\) 的离散化的值的逆替换为 \(y\),时间复杂度 \(O(1)\)。
-
\(x,y\) 均存在:将 \(x\) 的信息暴力合并到 \(y\) 上,由于数的种类最多 \(B\) 种,每次进行此类合并会减少一种,所以均摊时间复杂度为 \(O(nB)\)。
考虑如何查询:
-
块内查询:直接回答即可,时间复杂度 \(O(\frac{n}{B})\)。
-
跨块查询:记录上一个 \(x\) 和 \(y\) 的位置,扫到下一个 \(x\) 或 \(y\) 时记录答案,时间复杂度 \(O(\frac{n}{B})\)。
总时间复杂度 \(O(\frac{nm}{B}+nB)\),空间复杂度 \(O(nB)\)。
\(B\) 取 \(\sqrt n\) 时最优。
Optimization
-
快读快写。
-
调整数组维度,使得访问连续。
-
将数组降维。
-
维护两个值之间在块内最小距离时,只记录上三角,因为
"if" < "=" -
调整块长,\(B=200\) 时最优。
Code
Max Mem : 196.50 MiB
Average Time : 240 ms
Length : 3.29 KiB
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+9;
const int sN=509;
#define endl '\n'
inline int read(){
register int x=0;
register bool f=0;
register 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 f?-x:x;
}
char cr[200];int tt;
inline void print(register int x,register char k='\n') {
if(!x) putchar('0');
if(x < 0) putchar('-'),x=-x;
while(x) cr[++tt]=x%10+'0',x/=10;
while(tt) putchar(cr[tt--]);
putchar(k);
}
int a[N],n,m;
short blk[N];
int L[sN],R[sN];
short mp[N][sN],cnt[sN],val[N],Dis[N][sN];
int Lval[N],Rval[N];
inline void Chmin(short &x,short y){
if(x>y) x=y;
}
inline void Chmin(int &x,int y){
if(x>y) x=y;
}
int main(){
n=read();m=read();
for(int i=1;i<=n;i++) a[i]=read();
int B=200;
for(int i=1;i<=n;i++){
blk[i]=(i-1)/B+1;
if(!L[blk[i]]) L[blk[i]]=i;
R[blk[i]]=i;
}
memset(Dis,0x3f,sizeof(Dis));
for(int i=1;i<=blk[n];i++){
for(int j=L[i];j<=R[i];j++){
if(!mp[a[j]][i]) mp[a[j]][i]=++cnt[i],Lval[cnt[i]+L[i]]=j;
val[j]=mp[a[j]][i];Rval[val[j]+L[i]]=j;
}
for(int j=L[i];j<=R[i];j++){
for(int k=j;k<=R[i];k++){
if(val[j]<val[k]) Chmin(Dis[L[i]+val[j]][val[k]],k-j);
else Chmin(Dis[L[i]+val[k]][val[j]],k-j);
}
}
}
int lst=0;
while(m--){
int op=read(),x=read(),y=read();
x^=lst;y^=lst;
short X,Y;
if(op==1){
if(x==y) continue ;
for(register int i=1;i<=blk[n];i++){
X=mp[x][i];Y=mp[y][i];
if(!X) continue ;
if(!Y){
mp[y][i]=mp[x][i];
mp[x][i]=0;
continue ;
}
for(short j=1;j<=cnt[i];j++){
if(Y<j){
if(X<j) Chmin(Dis[L[i]+Y][j],Dis[L[i]+X][j]);
else Chmin(Dis[L[i]+Y][j],Dis[L[i]+j][X]);
}else{
if(X<j) Chmin(Dis[L[i]+j][Y],Dis[L[i]+X][j]);
else Chmin(Dis[L[i]+j][Y],Dis[L[i]+j][X]);
}
}
if(Lval[X+L[i]]<Lval[Y+L[i]]) Lval[Y+L[i]]=Lval[X+L[i]];
if(Rval[X+L[i]]>Rval[Y+L[i]]) Rval[Y+L[i]]=Rval[X+L[i]];
mp[x][i]=0;
}
}else{
int ans=n+1;
if(x!=y){
for(register int i=1,lx=-1,ly=-1;i<=blk[n];i++){
X=mp[x][i];Y=mp[y][i];
if(X&&Y){
if(X<Y) Chmin(ans,Dis[L[i]+X][Y]);
else Chmin(ans,Dis[L[i]+Y][X]);
}
if(X) if(~ly) Chmin(ans,Lval[X+L[i]]-ly);
if(Y) if(~lx) Chmin(ans,Lval[Y+L[i]]-lx);
if(X) lx=Rval[X+L[i]];
if(Y) ly=Rval[Y+L[i]];
if(ans<=1) break ;
}
}else for(int i=1;i<=blk[n];i++) if(mp[x][i]) ans=0;
if(ans==n+1) puts("Ikaros"),lst=0;
else print(ans),lst=ans;
}
}
}
Mistakes
-
没有算好数组大小,导致 MLE / RE。
-
常数太大。

浙公网安备 33010602011771号