前置知识
解法
正难则反,考虑求出总方案数和至少经过一个黑色格子的方案数,二者作差即为所求。
强制增加一个黑色格子 \((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
标签:格子,题解,ll,long,Grid,define,inv,dp,jc From: https://www.cnblogs.com/The-Shadow-Dragon/p/18282553