题意
一个 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下的结果
1 ≤ n , m ≤ 2000 1\le n,m\le2000 1≤n,m≤2000
题解
如果不能推动,那就差不多是过河卒了,这题也同样考虑 D P DP DP
考虑到推动会有向下向右的区别
设 D i , j / R i , j ( D o w n / R i g h t ) D_{i,j}/R_{i,j}(Down/Right) Di,j/Ri,j(Down/Right)表示从 ( 1 , 1 ) (1,1) (1,1)到 ( i , j ) (i,j) (i,j),上一步是向下 / / /右走的方案数
初始 D 1 , 1 = R 1 , 1 = 1 D_{1,1}=R_{1,1}=1 D1,1=R1,1=1
假设我们当前在 ( i , j ) (i,j) (i,j)且当前位置没有箱子,考虑向右最多能走多少步
假设从 ( i , j + 1 ) (i,j+1) (i,j+1)到 ( i , m ) (i,m) (i,m)上有 k k k个箱子,那么我们最多就能向右走 m − j − k m-j-k m−j−k步
即 ( i , j + 1 ) (i,j+1) (i,j+1)到 ( i , m − k ) (i,m-k) (i,m−k)这些格子我都能走到
由于是向右走过来的那么 R i , j + 1 , . . . , R i , m − k R_{i,j+1},...,R_{i,m-k} Ri,j+1,...,Ri,m−k的方案都要加上 R i , j + D i , j R_{i,j}+D_{i,j} Ri,j+Di,j
记 C n t D i , j CntD_{i,j} CntDi,j表示从 ( i , j ) (i,j) (i,j)向下到 ( n , j ) (n,j) (n,j)有多少箱子, C n t R i , j CntR_{i,j} CntRi,j表示从 ( i , j ) (i,j) (i,j)向右到 ( i , m ) (i,m) (i,m)有多少箱子
那么就有
R i , j + 1 , . . . , R i , m − C n t R i + 1 , j R_{i,j+1},...,R_{i,m-CntR_{i+1,j}} Ri,j+1,...,Ri,m−CntRi+1,j都要加上 R i , j + D i , j R_{i,j}+D_{i,j} Ri,j+Di,j
D i + 1 , j , . . . , D n − C n t D i + 1 , j , j D_{i+1,j},...,D_{n-CntD_{i+1,j},j} Di+1,j,...,Dn−CntDi+1,j,j都要加上 R i , j + D i , j R_{i,j}+D_{i,j} Ri,j+Di,j
区间加法可以使用差分数组进行优化,然后同一列 / / /行求和即可
因为差分只会影响到后面枚举的状态,所以可以直接在原数组上进行修改,然后求前缀和
最后答案就是 D n , m + R n , m D_{n,m}+R_{n,m} Dn,m+Rn,m
const int Mod=1e9+7;
inline void Plus(int&a,int b){a+=b;a>=Mod?a-=Mod:0;}
int main(){
...
for(int i=n;i;--i)
for(int j=m;j;--j)
CntD[i][j]+=CntD[i+1][j],CntR[i][j]+=CntR[i][j+1];
D[1][1]=R[1][1]=1;D[2][1]=R[1][2]=-1;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j){
Plus(D[i][j],D[i-1][j]);
Plus(R[i][j+1],D[i][j]),Plus(R[i][m-CntR[i][j+1]+1],Mod-D[i][j]);
Plus(R[i][j],R[i][j-1]);
Plus(D[i+1][j],R[i][j]),Plus(D[n-CntD[i+1][j]+1][j],Mod-R[i][j]);
}
Ans=(D[n][m]+R[n][m])%Mod;
...
}