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

你可能感兴趣的:(乘法逆元)