题目:求网格中点集A到点集B中不相交路径数
k个点(1,ai),到达(n,bi),只能向下或向右移动,问路径不相交的方案数
题解:先求出f(i,j)表示A的第i个点到B的第j个点的路径数,再解行列式det(f(i,j))即为解
Lindström–Gessel–Viennot引理
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N=100+10;
const int M=1000000007;
int n,K;
LL f[N][N],fac[200010],inv[200010];
int a[N],b[N];
LL C(LL a,LL b){return fac[a]*inv[b]%M*inv[a-b]%M;}
LL RP(LL a,LL b){LL Ans=1;for (;b;b>>=1,a=a*a%M)if (b&1)Ans=Ans*a%M;return Ans;}
void work()
{
scanf("%d%d",&n,&K);
for (int i=1;i<=K;i++)scanf("%d",&a[i]);
for (int i=1;i<=K;i++)scanf("%d",&b[i]);
for (int i=1;i<=K;i++)for (int j=1;j<=K;j++)
if (a[i]>b[j])f[i][j]=0;else f[i][j]=C(n+b[j]-a[i]-1,n-1);
int sign=1;
for (int i=1;i<=K;i++)for (int j=i+1;j<=K;j++){//辗转相除
int x=i,y=j;
while (f[y][i]){
LL d=f[x][i]/f[y][i];
for (int k=i;k<=K;k++)f[x][k]=(f[x][k]-f[y][k]*d%M+M)%M;
swap(x,y);
}
if (x!=i){
for (int k=i;k<=K;k++)swap(f[x][k],f[i][k]);
sign=-sign;//行列式行交换,值变为相反数
}
if (f[i][i]==0){printf("0\n");return ;}
}
LL Ans=(sign+M)%M;
for (int i=1;i<=K;i++)Ans=Ans*f[i][i]%M;
cout<<Ans<<endl;
}
int main()
{
//freopen("1.txt","r",stdin);
fac[0]=1;for (int i=1;i<200010;i++)fac[i]=fac[i-1]*i%M;
inv[0]=1;for (int i=1;i<200010;i++)inv[i]=RP(fac[i],M-2);
int Case;scanf("%d",&Case);
while (Case--)work();
return 0;
}