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

CodeForces - 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]; } }