题意
给你一个地图,你可以推箱子,不能推出界外,问从 到 的方案数
分析
一开始看完题目推箱子发现没有什么思路
我们抓住性质,如果一个格子,已经有一次从上面把箱子推下来了,就不可能从左边再推过去
其实我们走的就是横竖横竖这样的路线
再考虑一个问题,我们可以确定一个格子可以从左边过来,但是推了多少个箱子过来,我们是不是要记录一下是从哪个位置开始一直往左推的呢?
我们还可以想到,只要你拐弯了,一直直走只要箱子不撞墙,那么你拐弯前的答案就可以累加到拐弯后的状态上
不难得到 表示 从左边或者上边过来的方案数
转移:
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read()
{
ll p=0; ll f=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
const ll N = 2010;
const ll mod = 1e9+7;
ll dp[N][N][2],sum[N][N][2];
ll sumr[N][N],sumc[N][N];
char str[N][N];
int main()
{
// freopen("a.in","r",stdin);
ll n = read(); ll m = read();
if(n==1 && m==1){puts("1"); return 0;}
for(ll i=1;i<=n;i++) for(ll j=1;j<=m;j++) cin >> str[i][j];
for(ll i=1;i<=n;i++) for(ll j=1;j<=m;j++) sumr[i][j] = sumr[i][j-1] + (str[i][j] == 'R');
for(ll j=1;j<=m;j++) for(ll i=1;i<=n;i++) sumc[i][j] = sumc[i-1][j] + (str[i][j] == 'R');
for(ll i=1;i<=n;i++) for(ll j=1;j<=m;j++)
{
if(i==1) dp[i][j][0] = (sumr[i][m] <= (m-j));
if(j==1) dp[i][j][1] = (sumc[n][j] <= (n-i));
if(i!=1 && j!=1)
{
ll l = 1; ll r = j-1; ll ret = 0;
while(l<=r)
{
ll mid = (l+r)>>1;
if(sumr[i][m] - sumr[i][mid] <= m-j) ret = mid,r = mid - 1;
else l = mid + 1;
}
// printf("(%d,%d) 0 : %d\n",i,j,ret);
if(ret) dp[i][j][0] = (sum[i][j-1][1] - sum[i][ret-1][1] + mod) % mod;
l = 1; r = i-1; ret = 0;
while(l<=r)
{
ll mid = (l+r)>>1;
if(sumc[n][j] - sumc[mid][j] <= n-i) ret = mid,r = mid - 1;
else l = mid + 1;
}
// printf("(%d,%d) 1 : %d\n",i,j,ret);
if(ret) dp[i][j][1] = (sum[i-1][j][0] - sum[ret-1][j][0] + mod) % mod;
}
sum[i][j][0] = (sum[i-1][j][0] + dp[i][j][0]) % mod;
sum[i][j][1] = (sum[i][j-1][1] + dp[i][j][1]) % mod;
// printf("(%d,%d) %d %d %d %d\n",i,j,dp[i][j][0],dp[i][j][1],sumr[i][m],sumc[n][j]);
}
return printf("%lld\n",(dp[n][m][0] + dp[n][m][1]) % mod),0;
}
/*
left dp[i][j][0] = sigma(dp[i][y to j-1][1]) [sumr[i][m] - sumr[i][y] <= m-j+1]
up dp[i][j][1] = sigma(dp[x to i-1][j][0])
*/