杰拉尔德和巨型象棋
给定一个棋盘 每次可以向下或者向右移动一格 不能移动到某些禁止的位置 求移动到右下角的方案数
阶乘逆元的:
\[(i+1)! \equiv (i+1)! \times i\mod p
\]
\[(i!)^{-1} \equiv ((i+1)!)^{-1} \times (i+1) \mod p
\]
因此可以倒序求阶乘逆元
#include <iostream>
#include <cstdio>
#include <algorithm>
#define pii pair<int,int>
#define X first
#define Y second
#define ll long long
using namespace std;
const int N=2010;
const int M=2e5+10;
const int mod=1e9+7;
int read()
{
int x=0,f=0,c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return f?-x:x;
}
pii p[M];
int jc[M],invjc[M];
int f[M];
int ksm(int a,int b)
{
int ans=1;
while(b)
{
if(b&1) ans=(ll)ans*a%mod;
a=(ll)a*a%mod;
b=b>>1;
}
return ans;
}
//x里选择y个
int C(int x,int y)
{
if(y>x) return 0;
return (ll)jc[x]*invjc[x-y]%mod * invjc[y]%mod;
}
//f[i] i号点是第一个经过的黑点的方案数
int h,w,n;
int main()
{
h=read(); w=read(),n=read();
for(int i=1;i<=n;i++) p[i].X=read(),p[i].Y=read();
sort(p+1,p+n+1); p[n+1].X=h; p[n+1].Y=w;
jc[0]=1; for(int i=1;i<=h+w;i++) jc[i]=(ll)jc[i-1]*i%mod;
invjc[0]=1; invjc[h+w]=ksm( jc[h+w] , mod-2 );
for(int i=h+w-1;i>=1;i--) invjc[i]=(ll)invjc[i+1]*(i+1)%mod;
for(int i=1;i<=n+1;i++)
{
f[i]=C(p[i].X+p[i].Y-2,p[i].X-1);
for(int j=1;j<i;j++)
if(p[i].X>=p[j].X&&p[i].Y>=p[j].Y)
f[i]=(f[i]-(ll)f[j]*C(p[i].X-p[j].X+p[i].Y-p[j].Y,p[i].X-p[j].X)%mod +mod )%mod;
}
printf("%d",f[n+1]);
return 0;
}

浙公网安备 33010602011771号