首先将空地与空地之间连边,转化为一个无向图。
令dp[i][j][0/1/2/3]表示箱子在(i,j),人在箱子旁4个方向中的一个时是否存在方案(和NOIP2013华容道类似)
则有2种转移:dp[i][j][k]->dp[i][j][l](从方向k可以不通过(i,j)到方向l)
dp[i][j][k]->dp[i'][j'][k](人推动箱子)
最后答案就是枚举所有的(i,j),计算人的合法位置个数。
现在问题的关键就是判联通和计算人的合法位置个数。
这可以用tarjan实现。
时间复杂度O(nm)
#include<bits/stdc++.h>
#define maxn 1010
#define maxm 1001000
#define id(x,y) (((x)-1)*(m)+(y))
using namespace std;
struct edge{int r,nxt;}e[maxm<<3];
struct data{int x,y,v;}q[maxm*4];
int head[maxm],esz,sz,low[maxm],l,r;
int rig[maxm],tim,dfn[maxm],sx,sy,n,m,f[maxn][maxn][4],fa[maxm];
char s[maxn][maxn];
int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
void inline addedge(int u,int v){
e[++esz].r=v;e[esz].nxt=head[u];head[u]=esz;
e[++esz].r=u;e[esz].nxt=head[v];head[v]=esz;
}
int inline chkin(int x,int y){
return x>=1&&y>=1&&x<=n&&y<=m;
}
void inline tarjan(int u){
low[u]=dfn[u]=++tim;
for(int t=head[u];t;t=e[t].nxt)if(e[t].r!=fa[u])
if(!dfn[e[t].r]){
fa[e[t].r]=u,tarjan(e[t].r);
low[u]=min(low[u],low[e[t].r]);
} else low[u]=min(low[u],dfn[e[t].r]);
rig[u]=tim;
}
int get(int u,int v){
for(int t=head[u];t;t=e[t].nxt)
if(dfn[e[t].r]>dfn[u]&&dfn[e[t].r]<=dfn[v]&&dfn[v]<=rig[e[t].r])return e[t].r;
return 0;
}
bool cango(int x,int u,int v){
if(!dfn[u]||!dfn[v]||!dfn[x])return false;
int a=get(x,u),b=get(x,v);
if(a==b||(!a&&!b))return true;
if(!a&&low[b]<dfn[x])return true;
if(!b&&low[a]<dfn[x])return true;
if(low[b]<dfn[x]&&low[a]<dfn[x])return true;
return false;
}
bool isgood(int x,int y,int k){
int nx=x+dx[k],ny=y+dy[k];
if(!chkin(nx,ny)||s[nx][ny]=='#')return false;
return true;
}
int size(int u){
return rig[u]-dfn[u]+1;
}
int main(){
// freopen("A.in","r",stdin);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
scanf("%s",s[i]+1);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)if(s[i][j]!='#')
for(int k=0;k<4;++k){
int ni=i+dx[k],nj=j+dy[k];
if(!chkin(ni,nj)||s[ni][nj]=='#')continue;
if(id(i,j)<id(ni,nj))addedge(id(i,j),id(ni,nj));
}
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
if(s[i][j]=='X')sx=i,sy=j;
tarjan(id(sx,sy)),sz=tim;
for(int i=0;i<4;++i)if(isgood(sx,sy,i))
q[r++]=data{sx,sy,i},f[sx][sy][i]=1;
while(l<r){
data d=q[l++];
int x=d.x,y=d.y,v=d.v;
if(isgood(x+dx[v],y+dy[v],v)){
int nx=x+dx[v],ny=y+dy[v];
if(!f[nx][ny][v])f[nx][ny][v]=1,q[r++]=data{nx,ny,v};
}
for(int i=0;i<4;++i)if(!f[x][y][i]&&isgood(x,y,i)&&cango(id(x,y),id(x+dx[v],y+dy[v]),id(x+dx[i],y+dy[i])))
f[x][y][i]=1,q[r++]=data{x,y,i};
}
long long ans=0;
int z=id(sx,sy);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)if(s[i][j]=='.'){
int v[6]={0};
int nans=ans;
for(int k=0;k<4;++k)if(f[i][j][k])
for(int l=0;l<4;++l)if(cango(id(i,j),id(i+dx[k],j+dy[k]),id(i+dx[l],j+dy[l])))v[l]=1;
for(int k=0;k<4;++k)if(v[k]&&isgood(i,j,k)){
int x=id(i+dx[k],j+dy[k]);
if(!dfn[x])continue;
if(fa[x]==id(i,j)){
ans+=size(x);
if(dfn[x]<=dfn[z]&&dfn[z]<=rig[x])ans--;
}
if(low[x]<dfn[id(i,j)])v[4]=1;
}
if(v[4]){
int x=id(i,j);
ans+=sz-size(x);
if(dfn[z]<dfn[x]||rig[x]<dfn[z])ans--;
}
}
printf("%lld",ans);
}