第一周周训 1-1 E大大走格子
大大走格子
有一个h行w列的棋盘,里面有一些格子是不能走的,现在要求从左上角走到右下角的方案数。
Input
单组测试数据。
第一行有三个整数h, w, n(1 ≤ h, w ≤ 10^5, 1 ≤ n ≤ 2000),表示棋盘的行和列,还有不能走的格子的数目。
接下来n行描述格子,第i行有两个整数ri, ci (1 ≤ ri ≤ h, 1 ≤ ci ≤ w),表示格子所在的行和列。
输入保证起点和终点不会有不能走的格子。
Output
输出答案对1000000007取余的结果。
Sample Input
3 4 2
2 2
2 3
Sample Output
2
看到题以为是组合数的题,我们做过机器人走方格。现在有了黑点,考虑补集思想和容斥原理。那么最终答案=way[任意走]−way[途经至少一个黑点]+way[途经至少两个黑点]−way[途经至少三个黑点]...way[任意走]−way[途经至少一个黑点]+way[途经至少两个黑点]−way[途经至少三个黑点]...(其中way表示方案数)。而N=2000显然是不能枚举集合的,而且目前我所学的知识没办法解决,我们就需要本题容斥的特殊性了:不同对象之间存在依赖性与阶段性。也就是说我们选出至少两个黑点是在先选出一个黑点的基础上,考虑这个黑点还能到达哪些黑点从而得到的,以此类推。
我们所求的答案是走到 点(n,m)不经过任何一个黑点的路径数量;
我们先对点进行排序 // x升序 ,y升序
用 dp[i]表示从 (1,1)到第i个点不经过任何一个黑点的路径数量。
所以为(n,m)就是第 k+1 个点。
dp[i]=c(x1+y1-2,x1-1);//初始化
动态转移方程 dp[i]=(dp[i]-dp[j]*c(x1-x2+y1-y2,x1-x2)%mod+mod)%mod;
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const long long mod=1000000007;
ll jc[200050]= {1};
struct node
{
int x;
int y;
} black[2020];
bool cmp(node a,node b)
{
if(a.x==b.x)
return a.y<b.y;
return a.x<b.x;
}
ll power(ll a,ll b,ll c)
{
ll ans=1;
while(b>0)
{
if(b&1)
ans=ans*a%c;
a=a*a%c;
b=b>>1;
}
return ans;
}
ll c(ll a,ll b)
{
return jc[a]*power(jc[b]*jc[a-b]%mod,mod-2,mod)%mod;
}
int main()
{
for(ll i=1; i<=200000; i++) ///预处理
jc[i]=jc[i-1]*i%mod;
ll n,m,k,dp[2020];memset(dp,0,sizeof(dp));
cin>>n>>m>>k;
for(int i=1; i<=k; i++)
cin>>black[i].x>>black[i].y;
black[++k]=node{n,m};
sort(black+1,black+1+k,cmp);
for(int i=1; i<=k; i++)
{
ll x1=black[i].x,y1=black[i].y;
dp[i]=c(x1+y1-2,x1-1);
for(int j=1; j<i; j++)
{
ll x2=black[j].x,y2=black[j].y;
if(x2<=x1&&y2<=y1)///判断条件
dp[i]=(dp[i]-dp[j]*c(x1-x2+y1-y2,x1-x2)%mod+mod)%mod;
}
}
cout<<dp[k]<<endl;
return 0;
}
如果没有不明白的可以参考博客:https://blog.csdn.net/mrazer/article/details/52047436