CodeForces - 559C Gerald and Giant Chess

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yzyyylx/article/details/82884085

题面

题意

有一个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];
}
}

猜你喜欢

转载自blog.csdn.net/yzyyylx/article/details/82884085