hdu5852 Intersection is not allowed! 【矩阵行列式】

题意

给出\(n*n\)网格\((n<=10^5)\)
顶部有\(K\)个起点,底部有\(K\)个相对应的终点
每次只能向下或向右走
求有多少种从各个起点出发到达对应终点且路径不相交的路径?
\(10^9 + 7\)取模

题解

按照组合数的套路
二维空间从一个h * w的矩形左上角走到右下角只向下或向右走的方案数为\(C_{h + w}^{h}\)

这题直接求不好求,我们考虑容斥
可以发现,如果两个路径相交,就相当于交换了终点
那么我们枚举每个起点的终点,乘上一个容斥系数\((-1)^t\),其中\(t\)表示相交的路径个数,也可以看做选择的终点序列的逆序对个数

按照每个起点择每一个终点构造出一个组合数的矩阵,按照矩阵的的定义你会发现其实答案就等于这个矩阵的行列式

我们求一个行列式就做完了

这题太神了

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long int
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
using namespace std;
const int maxn = 105,maxm = 200005,INF = 1000000000,P = 1000000007;
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
    while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    return out * flag;
}
LL fac[maxm],inv[maxm],fv[maxm];
void init(){
    fac[0] = 1;
    for (int i = 1; i <= 200000; i++) fac[i] = fac[i - 1] * i % P;
    inv[0] = inv[1] = 1;
    for (int i = 2; i <= 200000; i++) inv[i] = (P - P / i) * inv[P % i] % P;
    fv[0] = 1;
    for (int i = 1; i <= 200000; i++) fv[i] = fv[i - 1] * inv[i] % P;
}
LL qpow(LL a,LL b){
    LL ans = 1;
    for (; b; b >>= 1,a = a * a % P)
        if (b & 1) ans = ans * a % P;
    return ans;
}
LL C(int n,int m){
    if (m > n) return 0;
    return fac[n] * fv[m] % P * fv[n - m] % P;
}
int n,K,a[maxn],b[maxn];
LL A[maxn][maxn],ans;
void gause(){
    ans = 1;
    for (int i = 1; i <= K; i++){
        int pos = i;
        while (A[pos][i] == 0 && pos <= K) pos++;
        if (pos > K){ans = 0; return;}
        if (pos != i) for (int j = i; j <= K; j++) swap(A[i][j],A[pos][j]),ans = -ans;
        for (int j = i + 1; j <= K; j++){
            LL t = A[j][i] * qpow(A[i][i],P - 2) % P;
            for (int k = i; k <= K; k++)
                A[j][k] = (A[j][k] - A[i][k] * t % P + P) % P;
        }
        ans = ans * A[i][i] % P;
    }
    ans = (ans % P + P) % P;
}
int main(){
    ios::sync_with_stdio(false);
    init();
    int T = read();
    while (T--){
        n = read(); K = read();
        REP(i,K) a[i] = read();
        REP(i,K) b[i] = read();
        REP(i,K) REP(j,K) A[i][j] = C(n - 1 + b[j] - a[i],n - 1);
        gause();
        cout << ans << endl;
    }
    return 0;
}

转载于:https://www.cnblogs.com/Mychael/p/8869833.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值