[Ynoi2018] そらのおとしもの

「弑尽破净的第四分块」

Luogu P5397

Solution (OldDiverTree)

Prob

伊卡洛斯给了你一个长为 \(n\) 的序列 \(a\)

你需要实现 \(m\) 个操作,操作有两种:

  1. 把序列中所有值为 \(x\) 的数的值变成 \(y\)
  2. 找出一个位置 \(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。

  • 常数太大。

posted @ 2025-03-11 20:56  JoeyJiang  阅读(10)  评论(0)    收藏  举报