1. 程式人生 > >559C Gerald and Giant Chess

559C Gerald and Giant Chess

題面

題意

有一個m*n的棋盤,其中有k個黑點,問從左上角向右下角走,每次只能向右或向下走,問有幾種不經過黑點的走法。

做法

首先因為n*m很大,而k較小,因此從k開始考慮演算法,首先可以發現從(a,b)->(c,d)只向右或向下的走法一共有C(b+d-a-c,b-a)種,然後考慮減去經過黑點的情況。 對於這類題目我們不用根據一共經過哪些黑點來考慮,而是可以考慮第一個經過的黑點是哪一個來分類,記dp[i]表示從左上角到第i個黑點不經過任何黑點的方案數,可以發現: dp[i]=ask(點(1,1),i)-dp[j]*ask(j,i)(j為所有在i左上角的點)。 ask(i,j)表示點i到點j的方案數(不考慮黑點)。 然後即可算出答案。

程式碼

#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
#define P pair<ll,ll>
#define mp make_pair
#define fi first
#define se second
#define N 2010
#define M 1000000007
using namespace std;

ll m,n,k,dp[N],jc[200100];
P num[N];

inline ll po(ll u,ll v)
{
	ll res=1;
	for(;v;)
	{
		if(v&1) res=res*u%M;
		u=u*u%M;
		v>>=1;
	}
	return res;
}

inline ll C(ll u,ll v){return jc[u]*po(jc[v],M-2)%M*po(jc[u-v],M-2)%M;}
inline ll ask(P u,P v){if(u.fi>v.fi || u.se>v.se) return 0;return C(v.fi+v.se-u.fi-u.se,v.fi-u.fi);}

int main()
{
	ll i,j,p,q;
	cin>>m>>n>>k;
	jc[0]=1;for(i=1;i<=200000;i++) jc[i]=jc[i-1]*i%M;
	for(i=1;i<=k;i++)
	{
		scanf("%lld%lld",&p,&q);
		if(p==q&&q==1 || p==m&&q==n)
		{
			puts("0");
			return 0;
		}
		num[i]=mp(p,q);
	}
	k++;
	num[k]=mp(m,n);
	sort(num+1,num+k+1);
	
	for(i=1;i<=k;i++)
	{
		dp[i]=ask(mp(1,1),num[i]);
		for(j=1;j<i;j++)
		{
			dp[i]=(dp[i]-dp[j]*ask(num[j],num[i])%M+M)%M;
		}
	}
	cout<<dp[k];
}
}