题目描述
mrazer在一个 n 行 m列的棋盘上, 坐标(x,y)代表第 x 行第 y 列的棋盘格子。
他要从(1,1)走到(n,m),每次只能向右走一格或向下走一格,不能走出棋盘。而
且棋盘上有些格子是不能走的。mrazer 想要选择两条从(1,1)走到(n,m)的路,这
两条路上的格子除了(1,1)和(n,m)不能有重复的,也就是说两条路在中间不能有
交点。他想知道这两条路的选择一共有多少种不同的方案。两种选择中有一条路
不同则视这两种选择不同。
输入格式
从文件 in sun. 中读入数据。
第一行输入两个整数n ,m 。
接下来n 行,每行输入一个长度为m 的 01串,表示一个字符矩阵。
矩阵中第i 行第 j 个字符表示(i,j)这个格子的能不能走。字符’0’表示能走,字
符’1’表示不能走。
数据保证字符矩阵的(1,1)和(n,m)位置为’0’。
输出格式
输出到文件 out sun. 中
输出一个整数,表示不同的选择数。因为这个答案可能很大,所以请输出答
案对 1000000007取模的结果。
样例输入
3 3
000
010
000
样例输出
1
数据范围
n,m<=5000
题解
这道题暴力DP应该比较好写
f[i][j][k]表示第一条路径到(i,j),第二条路径到(k,i+j-k)的合法的方案数
但是n,m<=5000,所以需要对思路进行转换
我们定义有限集合:
X0:从(2,1)走到(n,m-1)的不走1路径集合。
X1:从(1,2)走到(n-1,m)的不走1路径集合。
X2:从(2,1)走到(n-1,m)的不走1路径集合。
X3:从(1,2)走到(n,m-1)的不走1路径集合。
答案就是:
|{(x0,x1)|x0∈X0,x1∈X1 x0,x1的路径不相交}|
利用容斥原理
|{(x0,x1)|x0∈X0,x1∈X1}|−|{(x0,x1)|x0∈X0,x1∈X1 x0,x1的路径相交}|
然后我们定义
A=|{(x0,x1)|x0∈X0,x1∈X1 x0,x1的路径相交}|
B=|{(x2,x3)|x2∈X2,x3∈X3|
对 A 中的一个二元组(x0,x1),找到x0和x1最后相交的点,把这个点之后的x0和x1的路径互换一下,就组成新的(x2,x3)
对于A,B中的点对是一一对应的,所以|A|=|B|
那么最后的答案就是
|X0||X1|−|X2||X3|
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 5003
#define p 1000000007
#define LL long long
using namespace std;
int n,m;
LL f[N][N],g[N][N];
char s[N][N];
int main()
{
freopen("sun.in","r",stdin);
freopen("sun.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%s",s[i]+1);
f[2][1]=g[1][2]=1;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (s[i][j]!='1') {
if (i!=2||j!=1) f[i][j]=(f[i-1][j]+f[i][j-1])%p;
if (i!=1||j!=2) g[i][j]=(g[i-1][j]+g[i][j-1])%p;
}
LL ans=f[n][m-1]*g[n-1][m]%p-f[n-1][m]*g[n][m-1]%p;
ans=(ans%p+p)%p;
printf("%I64d\n",ans);
}