CodeForces - 559C Gerald and Giant Chess
阿新 • • 發佈:2018-11-02
題面
題意
有一個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];
}
}