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;
}
建议减紫

浙公网安备 33010602011771号