给出n∗n网格,顶部有k个起点,底部有k个相对应的终点
每次只能向下或向右走
求有多少种从各个起点出发到达对应终点且路径不相交的路径?(对109+7取模)
n*m空间从左上角走到右下角只走右或者下的方案数位C(n,n+m)
首先考虑两个棋子的情况,即一个棋子从a1到b1,另一个棋子从a2到b2,不考虑交叉方案数显然是C(b1-a1+n-1,n-1) * C(b2-a2+n-1,n-1)
对于路径交叉的方案,对于一个a1->b2,a2->b1的方案,这两条路径必然会相交,
把最后一个交叉点之后的两条路径交换就又对应一个a1->b1,a2->b2的路径交叉的方案
故我们建立了a1->b1,a2->b2交叉路径与a1->b2,a2->b1的方案的一一对应,那么不合法方案数就是C(b2-a1+n-1,n-1) * C(b1-a2+n-1,n-1)
多个棋子的情况,考虑容斥,如果两个路径相交,相当于交换了终点
枚举每个起点的终点,乘上一个容斥系数(−1)t,t表示相交的路径个数,(也可以看做选择的终点序列的逆序对个数)
按照每个起点选择每一个终点构造出一个组合数的矩阵,按照矩阵的的定义答案就是这个矩阵的行列式
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=2e5+7; const ll mod=1000000007; int inv[maxn],e[107],s[107],fac[maxn]; ll a[107][107]; ll det(int n) { ll ans=1; int k=0; for(int i=0;i<n;i++) { for(int j=i+1;j<n;j++) { int x=i,y=j; while(a[y][i]) { ll t=a[x][i]/a[y][i]; for(int l=i;l<n;l++) a[x][l]=(a[x][l]-a[y][l]*t)%mod; swap(x,y); } if(x!=i) { for(int l=0;l<n;l++) swap(a[i][l],a[x][l]); k^=1; } } if(a[i][i]==0) return 0; else ans=ans*a[i][i]%mod; } if(k) ans=-ans; return (ans+mod)%mod; } void init() { inv[0]=inv[1]=1; fac[0]=fac[1]=1; for(int i=2;i<maxn;i++) { fac[i]=1ll*fac[i-1]*i%mod, inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod; } for(int i=2;i<maxn;i++) inv[i]=1ll*inv[i-1]*inv[i]%mod; } ll C(ll n,ll m) { if(m<0) return 0; return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod; } int main( ) { int t; cin>>t; init(); while(t--) { int n,k; cin>>n>>k; for(int i=0;i<k;i++) cin>>s[i]; for(int i=0;i<k;i++) cin>>e[i]; for(int i=0;i<k;i++) { for(int j=0;j<k;j++) { a[i][j]=C(n-1+e[j]-s[i],e[j]-s[i]); } } cout<<det(k)<<endl; } return 0; }
原文地址:https://www.cnblogs.com/wronin/p/11623714.html
时间: 2024-10-28 10:15:10