CF1225E. Rock Is Push
题意:
你在一个 n × m n\times m n×m矩阵中的 ( 1 , 1 ) (1,1) (1,1)点,要到达 ( n , m ) (n,m) (n,m)点,你只能向右或者向下走。矩阵中有些格子有石头,你走到一个有石头的点可以按照你的移动方向推动石头,如果那个点上也有一个石头,它就会被按相同方向推的更远,以此类推。但是石头不能被推出矩阵外。问有多少种到达方案,对 1 0 9 + 7 10^9+7 109+7取模。
思路:
非常巧妙的一道dp题。不看题解真的想不到。
如果没有障碍物,或者障碍物不能移动,那么是一道比较简单的问题。
我们可以考虑到,只有下方或者右方的石头会对我们的路线产生影响。
并且因为石头不能被推出墙外,所以我们可以得到,假设我在
(
i
,
j
)
(i,j)
(i,j)这一点,如果右边有
k
k
k个石头,而我想一直往右走的话,最多能走
m
−
j
−
k
m-j-k
m−j−k步,也就是最远走到
(
i
,
m
−
k
)
(i,m-k)
(i,m−k)点。我们用
r
[
i
]
[
j
]
r[i][j]
r[i][j]和
d
[
i
]
[
j
]
d[i][j]
d[i][j]表示
(
i
,
j
)
(i,j)
(i,j)点的右边和下边有多少个石头。
我们将向右和向下的步骤拆开,用
d
p
[
i
]
[
j
]
[
0
/
1
]
dp[i][j][0/1]
dp[i][j][0/1]表示在
(
i
,
j
)
(i,j)
(i,j)这一点时,向右/下走,并且与前一步行走方式不同的方案数。我们可以假设我们在
(
i
,
j
)
(i,j)
(i,j)点,这个时候,
d
p
[
i
]
[
j
]
[
0
]
dp[i][j][0]
dp[i][j][0]表示向右走的方案,我们可以一直向右走,直到走不了,在期间可以选择转向(向下走)。所以它的值取决于在
(
i
,
j
+
1
)
(i,j+1)
(i,j+1)到
(
i
,
m
−
r
[
i
]
[
j
]
)
(i,m-r[i][j])
(i,m−r[i][j])哪一点改变行走方向。
即,我们可以得到式子:
d
p
[
i
]
[
j
]
[
0
]
=
∑
k
=
1
m
−
j
−
r
[
i
]
[
j
]
d
p
[
i
]
[
j
+
k
]
[
1
]
dp[i][j][0]=\sum_{k=1}^{m-j-r[i][j]}dp[i][j+k][1]
dp[i][j][0]=k=1∑m−j−r[i][j]dp[i][j+k][1]
d
p
[
i
]
[
j
]
[
1
]
=
∑
k
=
1
n
−
i
−
d
[
i
]
[
j
]
d
p
[
i
+
k
]
[
j
]
[
0
]
dp[i][j][1]=\sum_{k=1}^{n-i-d[i][j]}dp[i+k][j][0]
dp[i][j][1]=k=1∑n−i−d[i][j]dp[i+k][j][0]
这样我们可以从
(
n
,
m
)
(n,m)
(n,m)点进行逆推,设
d
p
[
n
]
[
m
]
[
0
]
=
d
p
[
n
]
[
m
]
[
1
]
=
1
dp[n][m][0]=dp[n][m][1]=1
dp[n][m][0]=dp[n][m][1]=1,最后答案为
d
p
[
1
]
[
1
]
[
0
]
+
d
p
[
1
]
[
1
]
[
1
]
dp[1][1][0]+dp[1][1][1]
dp[1][1][0]+dp[1][1][1]。
对于上面的状态转移方程,每次暴力算显然是过于耗费时间,可以计算一个后缀和
s
u
m
r
[
i
]
[
j
]
sumr[i][j]
sumr[i][j]和
s
u
m
d
[
i
]
[
j
]
sumd[i][j]
sumd[i][j]。这样可以转换为:
d
p
[
i
]
[
j
]
[
0
]
=
s
u
m
d
[
i
]
[
j
+
1
]
−
s
u
m
d
[
i
]
[
m
−
r
[
i
]
[
j
+
1
]
+
1
]
dp[i][j][0]=sumd[i][j+1]-sumd[i][m-r[i][j+1]+1]
dp[i][j][0]=sumd[i][j+1]−sumd[i][m−r[i][j+1]+1]
d
p
[
i
]
[
j
]
[
1
]
=
s
u
m
r
[
i
+
1
]
[
j
]
−
s
u
m
r
[
n
−
d
[
i
+
1
]
[
j
]
+
1
]
[
j
]
dp[i][j][1]=sumr[i+1][j]-sumr[n-d[i+1][j]+1][j]
dp[i][j][1]=sumr[i+1][j]−sumr[n−d[i+1][j]+1][j]
s
u
m
r
[
i
]
[
j
]
=
s
u
m
r
[
i
+
1
]
[
j
]
+
d
p
[
i
]
[
j
]
[
0
]
sumr[i][j]=sumr[i+1][j]+dp[i][j][0]
sumr[i][j]=sumr[i+1][j]+dp[i][j][0]
s
u
m
d
[
i
]
[
j
]
=
s
u
m
d
[
i
]
[
j
+
1
]
+
d
p
[
i
]
[
j
]
[
1
]
sumd[i][j]=sumd[i][j+1]+dp[i][j][1]
sumd[i][j]=sumd[i][j+1]+dp[i][j][1]
注意特判
1
×
1
1\times1
1×1的情况。
代码:
#include<bits/stdc++.h>
#define pii pair<int,int>
#define int long long
#define cl(x,y) memset(x,y,sizeof(x))
#define ct cerr<<"Time elapsed:"<<1.0*clock()/CLOCKS_PER_SEC<<"s.\n";
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define all(x) x.begin(),x.end()
#define lson x<<1,l,mid
#define rson x<<1|1,mid+1,r
#define INF 1e18
const int N=2e3+10;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
const double eps=1e-8;
const double pi=acos(-1);
using namespace std;
char a[N][N];
int sumd[N][N]={0},sumr[N][N]={0},dp[N][N][2]={0},d[N][N]={0},r[N][N]={0};
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int n,m,i,j;
cin>>n>>m;
for(i=1;i<=n;i++)
{
cin>>a[i]+1;
for(j=1;j<=m;j++)
if(a[i][j]=='R')
d[i][j]=r[i][j]=1;
}
for(i=n;i>=1;i--)
for(j=m;j>=1;j--)
{
d[i][j]+=d[i+1][j];
r[i][j]+=r[i][j+1];
}
dp[n][m][0]=dp[n][m][1]=sumd[n][m]=sumr[n][m]=1;
if(n==1 && m==1)
{
cout<<1<<endl;
return 0;
}
for(i=n;i>=1;i--)
for(j=m;j>=1;j--)
{
if(i==n && j==m)
continue;
dp[i][j][0]=(sumd[i][j+1]-sumd[i][m-r[i][j+1]+1]+mod)%mod;
dp[i][j][1]=(sumr[i+1][j]-sumr[n-d[i+1][j]+1][j]+mod)%mod;
sumr[i][j]=(sumr[i+1][j]+dp[i][j][0])%mod;
sumd[i][j]=(sumd[i][j+1]+dp[i][j][1])%mod;
}
cout<<(dp[1][1][0]+dp[1][1][1])%mod<<endl;
return 0;
}