杰拉尔德和巨型象棋

给定一个棋盘 每次可以向下或者向右移动一格 不能移动到某些禁止的位置 求移动到右下角的方案数

阶乘逆元的:

\[(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;
}
posted @ 2022-03-09 19:17  __iostream  阅读(54)  评论(0)    收藏  举报