P3684 [CERC2016] 机棚障碍

P3684 [CERC2016] 机棚障碍 题解

题解一堆非必要解法,讲一下一个正常的正确时间复杂度的解法。

题意简述:给一张\(N\times N\)的网格图,网格中有一些障碍,执行一个“推箱子”的操作,询问\(M\)次,每次回答以某个点为中心到另一个点能推的箱子最大有多少?

数据范围\(2\le N \le 1000,1\le M\le 300000\)

洛谷虚标难度石锤

Solution

大型缝合怪,分步考虑。

首先,每个点,我们只关心以它为中心最大能通过的箱子大小。

这个貌似很好维护,考虑二分,我们可以\(O(1)\)用二维前缀和来判断一个范围内有没有障碍。

时间复杂度为\(O(N^2logN)\)

bool check(int x,int y,int k){
    int x1=x-k+1,x2=x+k-1,y1=y-k+1,y2=y+k-1;
    if(x1<1||x2>n||y1<1||y2>n)return 0;
    return sum(x1,y1,x2,y2)==0;
}
//in main
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)
    s[i][j]+=s[i][j-1]+s[i-1][j]-s[i-1][j-1]+g[i][j];
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){
    if(g[i][j])continue;
    int l=1,r=n/2;
    while(l<r){
        int mid=(l+r+1)>>1;
        if(check(i,j,mid))l=mid;
        else r=mid-1;
    }
    Max[i][j]=r*2-1;
}

然后我们要求出从\((x_1,y_1)\)\((x2,y2)\)路径中的最大瓶颈。通俗的来说,就是让经过的路径中经过的最小边权(瓶颈)最大。

这个东西可以用kruskal重构树很方便的维护,然后在重构树上跑lca就好了。

lca当然要树剖啦

时间复杂度为\(O(N^2+MlogN^2+N^2logN)\)

代码如下

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10,M=N*N*2;
const int dx[]={1,0,-1,0};
const int dy[]={0,1,0,-1};

int head[N*N*2],ver[M],nxt[M],tot=1;
void add(int x,int y){ver[++tot]=y,nxt[tot]=head[x],head[x]=tot;}

int n,m,s[N][N],Max[N][N],etop,node,fa[N*N*2],val[N*N*2],f[N*N*2],d[N*N*2],dfn[N*N*2],top[N*N*2],sz[N*N*2],to[N*N*2],cnt;
bool g[N][N];
char str[N][N];
struct Edge{int x,y,z;}e[N*N*4];
bool cmp(const Edge&a,const Edge&b){return a.z>b.z;}
int id(int x,int y){return (x-1)*n+y;}
int get(int x){return fa[x]==x?fa[x]:fa[x]=get(fa[x]);}
int sum(int x1,int y1,int x2,int y2){return s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1];}
bool check(int x,int y,int k){
    int x1=x-k+1,x2=x+k-1,y1=y-k+1,y2=y+k-1;
    if(x1<1||x2>n||y1<1||y2>n)return 0;
    return sum(x1,y1,x2,y2)==0;
}
void dfs1(int x,int father){
    f[x]=father,sz[x]=1,d[x]=d[father]+1;
    for(int i=head[x];i;i=nxt[i]){
        int y=ver[i];
        if(y==father)continue;
        dfs1(y,x);
        sz[x]+=sz[y];
        if(sz[y]>sz[to[x]])to[x]=y;
    }
}
void dfs2(int x,int t){
    dfn[x]=++cnt,top[x]=t;
    if(to[x])dfs2(to[x],t);
    for(int i=head[x];i;i=nxt[i]){
        int y=ver[i];
        if(y==to[x]||y==f[x])continue;
        dfs2(y,y);
    }
}
int lca(int x,int y){
    while(top[x]!=top[y]){
        if(d[top[x]]<d[top[y]])swap(x,y);
        x=f[top[x]];
    }
    return d[x]<d[y]?x:y;
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%s",str[i]+1);
    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)g[i][j]=str[i][j]=='#';
    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)
        s[i][j]+=s[i][j-1]+s[i-1][j]-s[i-1][j-1]+g[i][j];
    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){
        if(g[i][j])continue;
        int l=1,r=n/2;
        while(l<r){
            int mid=(l+r+1)>>1;
            if(check(i,j,mid))l=mid;
            else r=mid-1;
        }
        Max[i][j]=r*2-1;
    }
    //for(int i=1;i<=n;i++){for(int j=1;j<=n;j++)printf("%d ",Max[i][j]);puts("");}
    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)for(int k=0;k<4;k++){
        int a=i+dx[k],b=j+dy[k];
        if(a<1||a>n||b<1||b>n||g[a][b]||g[i][j])continue;
        e[++etop]={id(i,j),id(a,b),min(Max[i][j],Max[a][b])};
    }
    node=n*n;
    for(int i=1;i<=n*n*2;i++)fa[i]=i;
    sort(e+1,e+etop+1,cmp);
    for(int i=1;i<=etop;i++){
        int x=get(e[i].x),y=get(e[i].y);
        if(x==y)continue;
        fa[x]=fa[y]=++node;
        val[node]=e[i].z;
        add(node,x),add(node,y);
    }
    for(int i=node;i;i--)if(get(i)==i)dfs1(i,0),dfs2(i,i);
    scanf("%d",&m);
    while(m--){
        int x1,y1,x2,y2;scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        int idx=id(x1,y1),idy=id(x2,y2);
        if(get(idx)!=get(idy)||g[x1][y1]||g[x2][y2]){puts("0");continue;}
        printf("%d\n",val[lca(idx,idy)]);
    }
    return 0;
}

建议减紫

posted @ 2024-10-04 18:36  lichenyu_ac  阅读(29)  评论(0)    收藏  举报