吃豆子(组合数学)(逆元)
看到数据范围较大,不能直接算。但是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;
}