吃豆子(组合数学)(逆元)_计算
吃豆子(组合数学)(逆元)_计算_02
吃豆子(组合数学)(逆元)_数学_03

看到数据范围较大,不能直接算。但是k的个数比较小,所以考虑从k下手。

我们通过思考可以得出在一个\(n\times m\)的格子中走的步数是有规律的,把向下走看作A,向右走看作B,那么显然不同的路径个数就是A,B的不同排列个数。

在这里我们引用到可重复元素的排列个数公式:

假设现在有n个元素,对于第一类元素\(n_1\),有sum1个重复,第二类元素\(n_2\),有sum2个重复......第m类元素\(n_m\),有summ个重复,总方案数为:

\[\frac{n!}{sum_1!\times sum_2!\times ...\times sum_m!} \]

(具体其他的组合数学相关知识可以详见我的这篇整理qwq:戳我qwq

之后的做法很就简单了。
我们先按照x,y从小到大排序,然后分别计算相邻两个豆子之间的路径个数。然后因为是乘法原理的思想,所以ans乘起来即可。

注意排序之后如果有豆子在它前面豆子的右上方,显然ans==0;

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define MAXN 100010
#define mod 1000000007
using namespace std;
int n,m,k;
long long ans=1;
struct Node{int x,y;}node[MAXN];
bool cmp(struct Node x,struct Node y)
{
    if(x.y<y.y) return 1;
    else if(x.y==y.y) 
    {
        if(x.x<y.x) return 1;
        else return 0;
    }
    else return 0;
}
inline long long mul(long long x,long long p)
{
	long long cur_ans=1;
	while(p)
	{
		if(p&1) cur_ans=cur_ans*x%mod;
		x=x*x%mod;
		p>>=1;
	}
	return cur_ans;
}
inline void solve(int x1,int y1,int x2,int y2)
{
	int cnt1=max(x2-x1,y2-y1);
    int cnt2=min(x2-x1,y2-y1);
    long long cur=1;
    for(int i=cnt1+1;i<=cnt1+cnt2;i++)
        cur=i*cur%mod;
    for(int i=1;i<=cnt2;i++)
	    cur=mul(i,mod-2)%mod*cur%mod;
    ans=cur*ans%mod;
    return;
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=k;i++)
        scanf("%d%d",&node[i].x,&node[i].y);
    node[0].x=1,node[0].y=1;
    node[k+1].x=n,node[k+1].y=m;
    sort(node,node+1+k+1,cmp);
    for(int i=1;i<=k+1;i++)
    {
        if(node[i].x<node[i-1].x&&node[i].y>node[i-1].y)
        {
            printf("0\n");
            return 0;
        }
        solve(node[i-1].x,node[i-1].y,node[i].x,node[i].y);
    }
    printf("%lld\n",ans%mod);
    return 0;
}