2016MUTC9-1009 Intersection is not allowed!

题目:求网格中点集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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值