[分块or定期重构/三维BIT]2019牛客多校8-D Distance

本文探讨了在三维空间中,如何高效处理标记点的打标与查询操作,提出了分块/定期重构与三维BIT两种方法。分块/定期重构通过多源BFS计算最短路径,三维BIT则利用树状数组维护区间最小值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

n*m*h<=1e5的三维空间内有q<=1e5次操作,分两种:

1. 给点(x,y,z)打标记

2.询问离位置A(x,y,z)最近的标记点到A的最近曼哈顿距离。

两种做法:

1.分块/定期重构

如果所有的询问操作都在打标记后,那么从所有的标记点开始做一次多源bfs求最短路(每次可以走上下左右前后6个方向哦),则询问点的dis就是到离它最近的点的曼哈顿距离。

分块的话,就把所有q个操作分成q^1/2块,假设当前要处理的块是i,那么i之前的块中标记点全都做一遍bfs,那么对于i内的询问点,只需要比较(这个询问点的dis)  和 ( 块内该询问点之前的所有标记点之间的距离),每一块做一次bfs是nmhq^1/2,每次块内暴力一下复杂度是q,所以复杂度是q^1/2*(nmh+q^1/2)。大概是这样吧……

定期重构就是把标记点攒一攒,攒到一定程度了就bfs更新掉。那么更新掉了的点会通过dis体现,还攒着没更新的就直接和当前查询点比一下,这些距离和dis比一下取个最小的就是啦。

2.三维BIT

可以参考之前写的最近曼哈顿距离的博客,https://blog.youkuaiyun.com/Mr_Alice/article/details/99765632。

二维变三维而已啦~

然后用三维树状数组维护了一下区间最小值~(其实就是前缀最小值咳咳)

感觉这个做法超酷O口O

以下是代码:

分块——103ms

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int n,m,h,Q,top;
struct node{
    int x,y,z;
}st[maxn];
queue<node>q;
int dis[maxn];
int id(int x,int y,int z){ return (x-1)*m*h+(y-1)*h+z;}
int dir[6][3]={1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,1,0,0,-1};
void upd(){
    while(!q.empty()) q.pop();
    for(int i=0;i<top;i++){
        dis[id(st[i].x,st[i].y,st[i].z)]=0;
        q.push(st[i]);
    }
    while(!q.empty()){
        node now=q.front();q.pop();
        for(int i=0;i<6;i++){
            int nx=now.x+dir[i][0],ny=now.y+dir[i][1],nz=now.z+dir[i][2];
            if(nx<1||ny<1||nz<1||nx>n||ny>m||nz>h) continue;
            if(dis[id(nx,ny,nz)]>dis[id(now.x,now.y,now.z)]+1){
                dis[id(nx,ny,nz)]=dis[id(now.x,now.y,now.z)]+1;
                q.push({nx,ny,nz});
            }
        }
    }
    top=0;
}
int main(){
    scanf("%d%d%d%d",&n,&m,&h,&Q);
    int lim=sqrt(n*m*h);
    memset(dis,127,sizeof(dis));
    while(Q--){
        int op,x,y,z;
        scanf("%d%d%d%d",&op,&x,&y,&z);
        if(op==1){
            st[top++]={x,y,z};
        }else{
            int res=dis[id(x,y,z)];
            for(int i=0;i<top;i++){
                res=min(res,abs(x-st[i].x)+abs(y-st[i].y)+abs(z-st[i].z));
            }
            printf("%d\n",res);
        }
        if(top>=lim){
            upd();
        }
    }
    return 0;
}

三维BIT——164ms

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+15;
int n,m,h,q;
struct BIT{
    int d[maxn];
    void init(){memset(d,128,sizeof(int)*(n*m*h+5));}
    inline int getid(int i,int j,int k){return (i-1)*m*h+(j-1)*h+k;}
    inline void getmax(int &a,int b){ a=(b>a)?b:a;}
    inline void update(int x,int y,int z,int val){
       // printf(" x=%d y=%d z=%d val=%d\n",x,y,z,val);
        for(int i=x;i<=n;i+=i&-i)
            for(int j=y;j<=m;j+=j&-j)
                for(int k=z;k<=h;k+=k&-k)
                    getmax(d[getid(i,j,k)],val);//getid(i,j,k)这个位置被更新了一个值
    }
    inline int sum(int x,int y,int z){
        int res=-2e9;
        for(int i=x;i>0;i-=i&-i)//询问所有x'<=x,y'<=y,z'<=z范围内的最大值,此处x,x'等都是包括符号了的哦
            for(int j=y;j>0;j-=j&-j)
                for(int k=z;k>0;k-=k&-k)
                    getmax(res,d[getid(i,j,k)]);
        return res;
    }
}t[8];
int main(){
    scanf("%d%d%d%d",&n,&m,&h,&q);
    for(int i=0;i<8;i++) t[i].init();
    int op,x,y,z,xx,yy,zz,xxx,yyy,zzz,ans;
    while(q--){
        ans=2e9;
        scanf("%d%d%d%d",&op,&x,&y,&z);
        for(int i=0;i<8;i++){
            if(4&i) xx=xxx=x;//+1是因为树状数组下标不可以为负
            else xx=n-x+1,xxx=-x;
            if(2&i) yy=yyy=y;
            else yy=m-y+1,yyy=-y;
            if(1&i) zz=zzz=z;
            else zz=h-z+1,zzz=-z;
            if(op==1){
                t[i].update(xx,yy,zz,xxx+yyy+zzz);
            }else{
                ans=min(ans,xxx+yyy+zzz-t[i].sum(xx,yy,zz));
            }
        }
        if(op==2) printf("%d\n",ans);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值