题意:n*n矩阵,k个棋子在第一行,将他们移到最后一行,这k条路径不相交的方案数有多少
1.n条不相交路径(严格)
Lindstrom-Gessel-Viennot Lemma
对于一张无边权的DAG图,给定n个起点和对应的n个终点,这n条不相交路径的方案数为
det() (该矩阵的行列式)
其中e(a,b)为图上a到b的方案数
2.行列式计算
https://blog.csdn.net/zhoufenqin/article/details/7779707
ps:求阶乘的逆元的小trick
inv[maxn] = qk_mod(fac[maxn],mod - 2);
for(int i = maxn - 1;i >= 0;i --) inv[i] = inv[i + 1] * (i + 1) % mod;
#include <cstdio>
#include <iostream>
using namespace std;
const int maxn = 1e5 + 5;
const int mod = 1e9 + 7;
const int maxk = 100 + 2;
typedef long long ll;
int a[maxk],b[maxk];
ll m[maxk][maxk];
int n,k;
ll fac[maxn * 2];
ll inv[maxn * 2];//inv[i] = 记录i! % mod
ll qk_mod(ll a,ll b)
{
ll ans = 1;
while (b) {
if(b & 1) ans = ans * a % mod;
b >>= 1;
a = a * a % mod;
}
return ans;
}
void init()
{
fac[0] = 1;
for(int i = 1;i < maxn * 2;i ++) fac[i] = fac[i - 1] * i % mod;
inv[maxn * 2 - 1] = qk_mod(fac[maxn * 2 - 1],mod - 2);
for(int i = maxn * 2 - 2;i >= 0;i --) inv[i] = inv[i + 1] * (i + 1) % mod;
}
ll C(int n,int m)
{
if(n < m) return 0;
return fac[n] * inv[m] % mod * inv[n - m] % mod;
}
ll solve()
{
int sf = 1;
ll ans = 1;
for(int i = 1;i <= k;i ++){
for(int j = i + 1;j <= k;j ++){
int x = i,y = j;
while(m[y][i]){
ll t = m[x][i] / m[y][i];
for(int p = i;p <= k;p ++) m[x][p] = (m[x][p] - t * m[y][p]) % mod;
swap(x,y);
}
if(x != i){
for(int p = 1;p <= k;p ++) swap(m[x][p],m[y][p]);
sf = -sf;
}
}
if(m[i][i] == 0) return 0;
else ans = ans * m[i][i] % mod;
}
ans = (ans * sf % mod + mod) % mod;
return ans;
}
int main()
{
init();
int T;scanf("%d",&T);
while(T --)
{
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 ++){
m[i][j] = C(n - 1 + b[j] - a[i],n - 1);
}
}
printf("%lld\n",solve());
}
return 0;
}