1. 程式人生 > >Codeforces 559C Gerald and Giant Chess(DP+乘法逆元求大組合數)

Codeforces 559C Gerald and Giant Chess(DP+乘法逆元求大組合數)

先把黑塊按座標排序。
dp[i]表示到第i個黑塊且之前沒有經過黑塊的方案數,那麼每一個dp[i]中的方案都是完全不相同的。遞推的方法是dp[i]=C(xi+yi,xi)-sum(dp[j]*C(xi-xj+yi-yj,xi-xj))  (j<i)
dp[j]*C(xi-xj+yi-yj,xi-xj)就是從1,1經過第j個黑塊到i的路徑數,用總路徑數減去所有的這些不合法的路徑數就是dp[i],(剪去的路徑中沒有重複的)。


由於組合數太大,需要用乘法逆元預處理出每個階乘的逆元。模板在下面。
pair的預設排序規則就是先按key再按value從小到大。


注意預處理階乘要到2*100000


程式碼:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#include <map>
#include <algorithm>
#define LL long long
const int mod=1e9+7;
#include <vector>
LL fact[200200];
LL ifact[200200];
int h,w,n;
LL dp[2005];
pair<int,int> G[2005];


LL ex(LL a,LL e)
{
    if(e==0) return 1;
    LL res=ex(a,e/2);
    res*=res;
    res%=mod;
    if(e%2==1) res*=a;
    res%=mod;
    return res;
}


LL inv(LL a)
{
    return ex(a,mod-2);
}


LL C(int h,int w)
{
    if(h<0||w<0) return 0;
    LL res=fact[h+w];
    res*=ifact[h];
    res%=mod;
    res*=ifact[w];
    res%=mod;
    return res;
}
void init(){
    fact[0]=1;
    ifact[0]=1;
    for(int i=1; i<200005; i++)
    {
        fact[i]=fact[i-1]*i;
        fact[i]%=mod;
        ifact[i]=inv(fact[i]);
    }
}




int main(){
    scanf("%d%d%d",&h,&w,&n);
    init();
    for(int i=0;i<n;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        G[i]=make_pair(x,y);
    }
    sort(G,G+n);
    G[n]=make_pair(h,w);
    for(int i=0;i<=n;i++){
        int x=G[i].first-1,y=G[i].second-1;
        dp[i]=C(x,y);
        for(int j=0;j<i;j++){
            int xx=G[i].first-G[j].first;
            int yy=G[i].second-G[j].second;
            LL tmp=C(xx,yy)*dp[j];
            tmp%=mod;
            dp[i]-=tmp;
            while(dp[i]<0) dp[i]+=mod;
        }
    }
    printf("%lld\n",dp[n]);
}