HDU 5852:Intersection is not allowed!(行列式+逆元求组合数)

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<...<akn ,表示棋子起始的位置是 (1,a1),(1,a2),...,(1,ak)

     第三行包含K个整数: 1≤b1<b2<...<bkn ,表示棋子目标的位置是 (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+(ba),ba)

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)



参考资料:


1维基百科 - 行列式

2Lindströ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);
}



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值