AT_dp_y Grid 2 题解

题目传送门

前置知识

计数 DP | 排列组合

解法

正难则反,考虑求出总方案数和至少经过一个黑色格子的方案数,二者作差即为所求。

强制增加一个黑色格子 \((h,w)\),使得存在一条至少经过一个黑色格子的路径。

如果没有“不能移动到黑色格子中”的限制,那么就是一个简单的格路计数问题,方案数为 \(\dbinom{h+w-2}{h-1}\)

对黑色格子以行坐标为第一关键字,列坐标为第二关键字升序排序。

\(f_{i}\) 表示从左上角到排序后的第 \(i\) 个黑色格子的方案数,状态转移方程为 \(f_{i}=\dbinom{x_{i}+y_{i}-2}{x_{i}-1}-\sum\limits_{j=1}^{i-1}[x_{i} \ge x_{j}] \times [y_{i} \ge y_{j}] \times f_{j} \times \dbinom{x_{i}-x_{j}+y_{i}-y_{j}}{x_{i}-x_{j}}\)

最终,有 \(f_{n+1}\) 即为所求。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define ull unsigned long long
#define sort stable_sort 
#define endl '\n'
const ll p=1000000007;
ll f[3010],inv[200010],jc[200010],jc_inv[200010];
pair<ll,ll>a[3010];
ll C(ll n,ll m,ll p)
{
    return (n>=m&&n>=0&&m>=0)?(jc[n]*jc_inv[m]%p)*jc_inv[n-m]%p:0;
}
int main()
{
    ll h,w,n,i,j;  
    cin>>h>>w>>n;
    inv[1]=1;
    jc[0]=jc_inv[0]=jc[1]=jc_inv[1]=1;
    for(i=2;i<=h+w-2;i++)
    {
        inv[i]=(p-p/i)*inv[p%i]%p;
        jc[i]=jc[i-1]*i%p;
        jc_inv[i]=jc_inv[i-1]*inv[i]%p;
    }
    for(i=1;i<=n;i++)
    {
        cin>>a[i].first>>a[i].second;
    }
    n++;
    a[n]=make_pair(h,w);
    sort(a+1,a+1+n);
    for(i=1;i<=n+1;i++)
    {
        f[i]=C(a[i].first+a[i].second-2,a[i].first-1,p);
        for(j=1;j<=i-1;j++)
        {
            if(a[j].first<=a[i].first&&a[j].second<=a[i].second)
            {
                f[i]=(f[i]-f[j]*C(a[i].first-a[j].first+a[i].second-a[j].second,a[i].first-a[j].first,p)%p+p)%p;
            }
        }
    }
    cout<<f[n]<<endl;
    return 0;
}

后记

多倍经验:CF559C Gerald and Giant Chess

posted @ 2024-07-03 21:00  hzoi_Shadow  阅读(9)  评论(0编辑  收藏  举报
扩大
缩小