深受数学竞赛影响,一开始在想折线计数方法,想用容斥原理来搞定有不能取的点的情况,事实说明我是个傻吊。。。3000*3000,有可能全是不能取的点,这么多点用个毛线容斥。。。刷题少了!!!!肯定DP啊,而且方程是十分简单,但是题目中有个限制是两条路径不能相交,这就需要大神的指引了!题解给出的是rng_58给的高贵冷艳的Lindström–Gessel–Viennot 引理,见链接:http://en.wikipedia.org/wiki/Lindstr%C3%B6m%E2%80%93Gessel%E2%80%93Viennot_lemma。
表示没大看懂,更不要说证明了,但是会用就行,直接把结论拿过来,两个点集,A, B, 从A中一个点到B的所有路径中不相交的路径对数等于以下这个矩阵的行列式:
.
其中e(a, b)表示a 到 b的路径条数。所以答案是
我们取初始集合为A((0, 1), (1, 0)) B((n - 1), m - 2) (n - 2, m - 1)即可。
代码:
dp部分写成函数会减少代码量。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 3005
#define Mod 1000000007
using namespace std;
long long dp[2][N][N], m, n;
char s[N][N];
void go (int k, int x, int y){
int i, j;
if (s[x][y] == '.') dp[k][x][y] = 1;
for (i = x; i < n; ++i)
for (j = y; j < m; ++j){
if (s[i][j] == '.' && (i != x || j != y))
dp[k][i][j] = ((i >= 1 ? dp[k][i - 1][j] : 0) + (j >= 1 ? dp[k][i][j - 1] : 0)) % Mod;
}
}
int main(){
int i, j, k;
long long re;
cin >> n >> m;
for (i = 0; i < n; ++i) scanf("%s", s[i]);
go(0, 0, 1), go(1, 1, 0);
re = abs(dp[1][n -1][m - 2] * dp[0][n - 2][m - 1] % Mod - dp[1][n - 2][m - 1] * dp[0][n - 1][m - 2] % Mod + Mod) % Mod;
printf("%I64d\n", re);
return 0;
}