[CF559C] Gerald and Giant Chess

Description

给定一个 (H imes W) 的棋盘,有 (N) 个障碍格子,棋子每次可以向右或者向下移动一步,求从左上角到右下角有多少种不同的路线。(N le 2000)

Solution

由于 (N) 较小,考虑 dp,设 (f[i]) 表示走到第 (i) 个障碍点(且之前不经过其它任何障碍点)的方案数,则

[f[i]=g(x_i,y_i) - sum_{j o i} g(x_i-x_j+1,y_i-y_j+1 )cdot f[j] ]

其中 (j o i Leftrightarrow x_j le x_i and y_j le y_i)(g(x,y)) 表示无障碍情况下 ((1,1) o (x,y)) 的方案数,有

[g(x,y)=inom {x+y-2}{x-1} ]

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 2005;
const int M = 200005;
const int mod = 1e9+7;

int f[N],frac[M],ifrac[M],h,w,x[N],y[N],n;

struct pii
{
    int x,y;
    bool operator < (const pii &obj)
    {
        if(x==obj.x) return y<obj.y;
        return x<obj.x;
    }
} buf[N];

int qpow(int p,int q)
{
    return (q&1 ? p : 1) * (q ? qpow(p*p%mod,q>>1) : 1) % mod;
}

int inv(int p)
{
    return qpow(p,mod-2);
}

int nCr(int n,int m)
{
    return frac[n]*ifrac[m]%mod*ifrac[n-m]%mod;
}

int g(int x,int y)
{
    return nCr(x+y-2,x-1);
}

signed main()
{
    ios::sync_with_stdio(false);

    cin>>h>>w>>n;
    for(int i=1;i<=n;i++) cin>>x[i]>>y[i];

    frac[0]=ifrac[0]=1;
    for(int i=1;i<M;i++) frac[i]=frac[i-1]*i%mod;
    for(int i=1;i<M;i++) ifrac[i]=inv(frac[i]);

    for(int i=1;i<=n;i++) buf[i].x=x[i], buf[i].y=y[i];
    sort(buf+1,buf+n+1);
    for(int i=1;i<=n;i++) x[i]=buf[i].x, y[i]=buf[i].y;

    f[0]=1;
    x[0]=y[0]=1;
    x[n+1]=h;
    y[n+1]=w;
    for(int i=1;i<=n+1;i++)
    {
        f[i]=g(x[i],y[i]);
        for(int j=1;j<i;j++)
        {
            if(x[i]>=x[j] && y[i]>=y[j]) f[i]-=g(x[i]-x[j]+1,y[i]-y[j]+1)*f[j];
            f[i]%=mod;
            f[i]+=mod;
            f[i]%=mod;
        }
    }

    cout<<f[n+1]<<endl;
}

原文地址:https://www.cnblogs.com/mollnn/p/13589059.html