BZOJ3919 : [Baltic2014]portals
预处理出每个点上下左右能延伸到的最远点以及到它们的距离的最小值md。
然后spfa,一个点除了可以以1的代价到达四周的点之外,还可以以md+1的代价到达四个方向能到达的最远点。
#include<cstdio> const int N=1010,M=4194303; int n,m,i,j,z,x,y,loc[N][N][4],dis[N][N][4],md[N][N],d[N][N],q[M+1][2],h,t; bool a[N][N],in[N][N];char s[N]; inline void add(int x,int y){ if(!a[x][y]||d[x][y]<=z)return; d[x][y]=z; if(!in[x][y]){ in[x][y]=1; if(z<d[q[h][0]][q[h][1]])h=(h+M)&M,q[h][0]=x,q[h][1]=y;else t=(t+1)&M,q[t][0]=x,q[t][1]=y; } } int main(){ for(scanf("%d%d",&n,&m),i=1;i<=n;i++)for(scanf("%s",s+1),j=1;j<=m;j++){ a[i][j]=s[j]!='#',d[i][j]=~0U>>1; if(s[j]=='S')d[i][j]=0,h=t=in[i][j]=1,q[1][0]=i,q[1][1]=j; if(s[j]=='C')x=i,y=j; } for(i=1;i<=n;i++)for(j=1;j<=m;j++)if(a[i][j]){ if(a[i-1][j])loc[i][j][0]=loc[i-1][j][0],dis[i][j][0]=dis[i-1][j][0]+1;else loc[i][j][0]=i; if(a[i][j-1])loc[i][j][1]=loc[i][j-1][1],dis[i][j][1]=dis[i][j-1][1]+1;else loc[i][j][1]=j; } for(i=n;i;i--)for(j=m;j;j--)if(a[i][j]){ if(a[i+1][j])loc[i][j][2]=loc[i+1][j][2],dis[i][j][2]=dis[i+1][j][2]+1;else loc[i][j][2]=i; if(a[i][j+1])loc[i][j][3]=loc[i][j+1][3],dis[i][j][3]=dis[i][j+1][3]+1;else loc[i][j][3]=j; md[i][j]=dis[i][j][0]; if(md[i][j]>dis[i][j][1])md[i][j]=dis[i][j][1]; if(md[i][j]>dis[i][j][2])md[i][j]=dis[i][j][2]; if(md[i][j]>dis[i][j][3])md[i][j]=dis[i][j][3]; } while(((t+1)&M)!=h){ i=q[h][0],j=q[h++][1],h&=M,in[i][j]=0,z=d[i][j]+1; add(i-1,j),add(i+1,j),add(i,j-1),add(i,j+1); z+=md[i][j]; add(loc[i][j][0],j),add(i,loc[i][j][1]),add(loc[i][j][2],j),add(i,loc[i][j][3]); } return printf("%d",d[x][y]),0; }