考试考到这个了,本来考虑了容斥,爆炸欧鸡上面有一道叫 两双手 的类似的题,然后打了打发现貌似不好做,然后就开爆搜了qwq,现在恶补一下。
我们发现有一种东西叫做(Lindström–Gessel–Viennot lemma),上网搜一下,你就会发现你看不懂
这个定理就是求一个点集到另一点集的不相交路径,定理本质也还是容斥
这道题给你一个n*m的矩阵((1sim n, 1sim m)),现在有两只乌龟都从左上角(1,1)出发,到达右下角(n,m),乌龟每次只能往右或者往下走一格,且两只乌龟走的路径不能交叉,问一共有多少种走法。
首先显然可知的是一只乌龟必然从(1,2)走到(n-1, m),另一只必然从(2,1)走到(n,m-1),因为不能相交,
(有点像卡特兰数在坐标系中的意义)
根据定理,相当于求一个(2*2)行列式的值(都会吧qwq)
[left|egin{array}{cccc}
f_1 & f_2 \
f_3 & f_4\
end{array}
ight| \
其中
f_1=(2,1)->(n,m-1)的方案数\
f_2=(2,1)->(n-1,m)的方案数\
f_3=(1,2)->(n,m-1)的方案数\
f_4=(1,2)->(n-1,m)的方案数\
]
两遍dp分别可以求出四个方案,代入行列式中,就可以求出(2,1)到(n,m-1),(1,2)到(n-1,m)且路径不相交的方案数了,(ans=(f_1*f_4)-(f_2*f_3))
注意,记得取模qwq,允许的情况下,多取模一定没坏处qwq
代码锅了咕咕咕 一会补上
#include<cstdio>
#include<iostream>
#include<cstring>
#define maxn 3005
#define int long long
using namespace std;
const int mod = 1e9 + 7;
int n,m,az[maxn][maxn];
char a[maxn][maxn];
inline int add(int x,int y){if(x+y < 0) return x+y+mod;return x+y >= mod ? x+y-mod : x+y;}
int mul(int x, int y) {return 1ll * x * y % mod;}
inline void init(){
scanf("%lld%lld",&n,&m);
for(int i = 1;i <= n;i++)
for(int j = 1;j <= m;j++)
cin >> a[i][j];
}
inline int f(int x1,int y1,int x2,int y2){
memset(az,0,sizeof(az));
for(int i = x1;i <= x2;i++)
for(int j = y1;j <= y2;j++)
if(a[i][j] == '0') {
if(i == x1 && j == y1) az[i][j] = 1;
else az[i][j] = add(az[i - 1][j],az[i][j - 1]);
}
return az[x2][y2];
}
signed main(){
init();
int ans=add(mul(f(1,2,n-1,m),f(2,1,n,m-1)),-mul(f(1,2,n,m-1),f(2,1,n-1,m)));
printf("%lld",ans);
}
交学长的数据A了,在CF上过了样例但是WA了,咕咕咕