牛客网多校1 A Monotonic Matrix(数论阶乘逆元+组合数学不相交路径定理)

链接:https://www.nowcoder.com/acm/contest/139/A
来源:牛客网
 

题目描述

Count the number of n x m matrices A satisfying the following condition modulo (109+7).
* Ai, j ∈ {0, 1, 2} for all 1 ≤ i ≤ n, 1 ≤ j ≤ m.
* Ai, j ≤ Ai + 1, j for all 1 ≤ i < n, 1 ≤ j ≤ m.
* Ai, j ≤ Ai, j + 1 for all 1 ≤ i ≤ n, 1 ≤ j < m.

输入描述:

The input consists of several test cases and is terminated by end-of-file.
Each test case contains two integers n and m.

输出描述:

For each test case, print an integer which denotes the result.

示例1

输入

复制

1 2
2 2
1000 1000

输出

复制

6
20
540949876

备注:

 

* 1 ≤ n, m ≤ 103
* The number of test cases does not exceed 105.

#include<iostream>
#include<algorithm>
#include<string>
#include<cmath>//int dx[4]={0,0,-1,1};int dy[4]={-1,1,0,0};
#include<set>//int gcd(int a,int b){return b?gcd(b,a%b):a;}
#include<queue>
#include<cmath>
#include<stack>
#include<string.h>
#include<stdlib.h>
#include<cstdio>
#define ll unsigned long long
#define maxn 2005
#define MAX 500005
#define ms memset
using namespace std;
#pragma comment(linker, "/STACK:1024000000,1024000000") ///在c++中是防止暴栈用的
const int mod=(1e9+7);

/*
题目大意:给定一个矩阵的规模,
计数用0,1,2来填充的个数,
其中满足数学要求。。。(题目中见)

首先要有组合数学不相交路径的知识,(下面转发的)
Lindström–Gessel–Viennot Lemma
作为一个特殊情况,引理提供对给定一个起点终点对集(记起点集为 AA 终点集为 BB ,且路径为 ai→biai→bi ),统计不相交路径数。
引理的内容如下:
令 ω(P):=ω(P):= 路径 PP 的边权积, e(a,b):=∑P:a→bω(P)e(a,b):=∑P:a→bω(P) ,
 
M:=(e(a1,b1)e(a1,b2)⋯e(a1,bm) e(a2,b1)e(a2,b2)⋯e(a2,bm) ⋮⋮⋱⋮ e(an,b1)e(an,b2)⋯e(an,bm))M:=(e(a1,b1)e(a1,b2)⋯e(a1,bm) e(a2,b1)e(a2,b2)⋯e(a2,bm) ⋮⋮⋱⋮ e(an,b1)e(an,b2)⋯e(an,bm))

该引理宣称,det|M|det|M| 给出AA 到 BB 不相交路径的权值和。
特殊情况,令各边边权为11 ,此时|M||M| 为不相交路径数。
更一般情况,ωω 可以是形式变量,则 ee 将成为形式幂级数。
假定我们在网格图中,从第 11 行的起点走到第 nn 的终点。
首先假设我们只有两对点 (a1,b1),(a2,b2)(a1,b1),(a2,b2) ,不考虑相交,则总路径数为 (b1−a1+n−1n−1)(b2−a2+n−1n−1)(b1−a1+n−1n−1)(b2−a2+n−1n−1) .
考虑 a1→b1,a2→b2a1→b1,a2→b2 相交,则在最后一个相交点之后交换路径,则得到 a1→b2,a2→b1a1→b2,a2→b1 的路径。
考虑 a1→b2,a2→b1a1→b2,a2→b1 的路径,其必相交,则在最后一个相交点之后交换路径。
此时我们建立了 a1→b1,a2→b2a1→b1,a2→b2 的相交路径与 a1→b2,a2→b1a1→b2,a2→b1 的一一对应。
所以除去不合法情况后,答案为 (b1−a1+n−1n−1)(b2−a2+n−1n−1)−(b1−a2+n−1n−1)(b2−a1+n−1n−1)(b1−a1+n−1n−1)(b2−a2+n−1n−1)−(b1−a2+n−1n−1)(b2−a1+n−1n−1) .
接着考虑多组路径,使用容斥原理。
假设某些路径相交,则在最后一个相交点之后交换路径,得到 BB 的重排 CC .
所以我们只需考虑 CC 的逆序对数,为奇数则贡献为负,否则为正。
展开写,发现这就是一个行列式。

有了这个,可以从四个点的坐标,(n,0),(n-1,-1),(0,m),(-1,m-1)四个点分别为起点和终点。
那么行列式就显然了。

下面我们要计算逆元,由于阶乘的逆元的特殊,所以用快速幂打表即可。
*/

ll Pow(ll a,ll b)
{
    ll t=1;
    while(b>0)
    {
        if(b&1) t=t*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return t;
}

ll jie[maxn],inv[maxn];
void get_jie()
{
    jie[0]=1;
    for(int i=1;i<maxn;i++)
        jie[i]=(jie[i-1]*i)%mod;
}

void get_inv()
{
    for(int i=0;i<maxn;i++)
       inv[i]= Pow(jie[i],mod-2);
}

ll con(ll n,ll m)
{
    return (jie[n]*inv[m]%mod)*inv[n-m]%mod;
}

ll n,m,ans,tp;

int main()
{
    get_jie();
    get_inv();
    while(scanf("%lld%lld",&n,&m)!=EOF)
    {
        ans=con(n+m,m);

        ans=ans*ans%mod;

        ans=ans-con(n+m,m-1)*con(n+m,n-1)%mod;

        ans=(ans+mod)%mod;

        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37451344/article/details/81156857