Description
有K个棋子在一个大小为N×N的棋盘。一开始,它们都在棋盘的顶端,它们起始的位置是 (1,a1),(1,a2),...,(1,ak) ,它们的目的地是 (n,b1),(n,b2),...,(n,bk)。
一个位于 (r,c) 的棋子每一步只能向右走到 (r,c+1) 或者向下走到 (r+1,c) 。
我们把 i 棋子从 (1,ai) 走到 (n,bi) 的路径记作 pi 。
你的任务是计算有多少种方案把n个棋子送到目的地,并且对于任意两个不同的棋子 i,j ,使得路径 pi 与 pj 不相交(即没有公共点)。
Input
第一行为两个整数 N,K(1≤N≤105,1≤K≤100) ,表示棋盘的大小和棋子的数目。
第二行包含K个整数: 1≤a1<a2<...<ak≤n ,表示棋子起始的位置是 (1,a1),(1,a2),...,(1,ak)
第三行包含K个整数: 1≤b1<b2<...<bk≤n ,表示棋子目标的位置是 (1,b1),(1,b2),...,(1,bk)
Output
输出一个整数,表示答案对 109+7 取模的值。
Sample Input
【样例输入1】
3 2
1 2
2 3
【样例输入2】
5 2
1 2
3 4
Sample Output
【样例输出1】
3
【样例输出2】
50
HINT
【样例解释】
【数据范围与约定】
测试点1-2: N,K≤5
测试点3-4: N≤105,K=2
测试点5-6: N≤105,K=3
测试点7-10: N≤105,K≤100
本题做法:行列式求值。
令
M=⎡⎣⎢⎢⎢⎢e(a1,b1)e(a2,b1)...e(ak,b1)e(a1,b2)e(a2,b2)...e(ak,b2)e(a1,b3)e(a2,b3)...e(ak,b3)............e(a1,bk)e(a2,bk)...e(ak,bk)⎤⎦⎥⎥⎥⎥
其中,e(a,b) 为从顶部的(1,a)走到底部的(n, b)的方案数,即 C(n−1+(b−a),b−a)
det(M) 就是本题的答案。
为什么呢?下面给出证明。
为了方便表示,我们用 Pi 表示从 ai 到 bi 的一条路径,其权值 ω(pi)=e(ai,bi),把 P=(P1,P2,...,Pk) 记作 k-path ,权值 ω(P)=ω(P1)ω(P2)...ω(Pk)
根据行列式的定义,
det(M)=∑σ∈Snsign(σ)⋅e(a1,bσ(1))e(a2,bσ(2))...e(ak,bσ(k))
=∑σ∈Snsign(σ)⋅ω(P)WHERE P is a k-path from (a1,a2,...,an)to(bσ(1),bσ(2),...,bσ(k))
要证明 det(M) 为 P 中所有不相交的k_path的方案数,只需证明:
∑P为有相交的kpathsign(σ(P))ω(P)=0
(因为P中所有不相交的k_path方案数必然满足 σ(i)=i ,也必有 sign(σ)=1)
对于任意一条有交点的k_path P=(P1,P2,...,Pk) ,我们选择一个有交点的路径 Pi (若有多个,取i最小的),并沿着 Pi 走到最近的交点 m ,并选择一个经过交点 m 的路径 Pj (若有多个,取j最大的)。
我们定义 f(P) 为一个k_path,只不过将 Pi,Pj 在交点以后的地方交换(也就是说 σ(f(P)) 是 σ(P) 交换了 σ(i) 和 σ(j) 的结果);因此 sign(σ(f(P)))=−sign(σ(P)) ,又由于 f(P) 与 P 都有同样多的边集,故 ω(f(P))=ω(P) 。
从上面可以看出,P 与 f(P) 是一一对应的。
故对于任意一个 P ,都有 sign(σ(P))ω(P)+sign(σ(f(P)))ω(f(P))=0
所以
∑P为有相交的kpathsign(σ(P))ω(P)=0
故最终答案为 det(M)
参考资料:
2、Lindström–Gessel–Viennot lemma- Wikipedia, the free encyclopedia
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
using namespace std;
const int mod=1e9+7;
const int maxn=2e5+50;
const int maxk=1e2+20;
int fac[maxn],inv[maxn];
int g[maxk][maxk],a[maxk],b[maxk];
inline int power(int x,int y)
{
int ret=1;
while(y)
{
if(y&1)ret=1ll*ret*x%mod;
x=1ll*x*x%mod;
y>>=1;
}
return ret;
}
inline void pre()
{
fac[0]=1;
for(int i=1;i<=maxn;i++)fac[i]=1ll*i*fac[i-1]%mod;
inv[maxn]=power(fac[maxn],mod-2);
for(int i=maxn-1;i>=0;i--)inv[i]=1ll*inv[i+1]*(i+1)%mod;
}
int n,k,ans=1,fg=1;
inline int C(int _n,int _m)
{
return 1ll*fac[_n]*inv[_m]%mod*inv[_n-_m]%mod;
}
inline void guass()
{
for(int i=1;i<=k;i++)
{
if(g[i][i]==0)
{
for(int j=i+1;j<=k;j++)
if(g[j][i])
{
fg*=(-1);
for(int _=i;_<=k;_++)
{
swap(g[i][_],g[j][_]);
}
}
}
if(g[i][i]==0)
{
ans=0;
return;
}
ans=1ll*ans*g[i][i]%mod;
for(int j=i+1;j<=k;j++)
if(g[j][i])
{
int tmp=1ll*g[j][i]*power(g[i][i],mod-2)%mod;
for(int _=i;_<=k;_++)g[j][_]=(g[j][_]-1ll*tmp*g[i][_]%mod+mod)%mod;
}
}
}
int main()
{
pre();
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(b[j]<a[i])g[i][j]=0;
else g[i][j]=C(n-1+b[j]-a[i],n-1);
}
guass();
printf("%d\n",ans*fg);
}