题目描述:一个h*w( 1<=h,w<=105 )的棋盘,有n( 1<=n<=2000 )个格子不能走。求从 (1,1) 到 (h,w) 有多少条路径(对 109+7 取膜)。
做过马拦过河卒就知道用dp做。请注意数据范围!!
这道题之所以难,是因为数据大,用dp(
O(h∗w)
)做会无限TLE(虽然时间上限是2s)。
然后身为蒟蒻的我就果断地放弃了……(毕竟是ACM制骗不了分的) /(ㄒoㄒ)/~
之后看了大神们的题解才做出来的。
采用补集的思想。
令dp[i]表示合法地走到第i个点的路径数。那么总路径数(不计合不合法)为
Cx−1x+y−2
不合法即经过了那些不能走的格子,这样的路径有
∑dp[j]∗Cxi−xjxi+yi−xj−yj
dp[i]=Cxi−1xi+yi−2−∑dp[j]∗Cxi−xjxi+yi−xj−yj
在计算组合数时,现将阶乘以及它的逆元预处理出来。
计算逆元的方法有很多,在这里我用的是费马小定理
ap−1≡1
(
mod
p),其中p是素数。
这样看来这道题不算太难,然而我放弃了……
蒟蒻加油 ↖(^ω^)↗
附代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#define LL long long int
#define MAXN 2050
#define MAXM 200050
#define mod 1000000007
using namespace std;
int h ,w ,n ;
LL dp[MAXN] ,fact[MAXM] ,ni[MAXM] ;
struct node
{
int x ,y ;
bool operator < (const node &a) const
{
return x+y<a.x+a.y;
}
}point[MAXN] ;
LL power(LL a,LL pos)
{
LL ans=1;
while(pos>0)
{
if(pos&1)
ans=ans*a%mod;
a=a*a%mod;
pos>>=1;
}
return ans;
}
void init()
{
fact[0]=ni[0]=fact[1]=ni[1]=1;
for(int i=2;i<=200000;++i)
{
fact[i]=fact[i-1]*i%mod;
ni[i]=power(fact[i],mod-2);
}
}
LL C(int a,int b)
{
return fact[a]*ni[b]%mod*ni[a-b]%mod;
}
int main()
{
init();
while(~scanf("%d%d%d",&h,&w,&n))
{
for(int i=0;i<n;++i)
{
scanf("%d%d",&point[i].x,&point[i].y);
--point[i].x ,--point[i].y;
}
point[n].x=h-1 ,point[n].y=w-1 ;
sort(point,point+n+1);
int x=point[0].x ,y=point[0].y ,u ,v ;
dp[0]=C(x+y,x);
for(int i=1;i<=n;++i)
{
x=point[i].x ,y=point[i].y ;
dp[i]=C(x+y,x);
for(int j=0;j<i;++j)
{
u=point[j].x ,v=point[j].y ;
if(v>y||u>x)continue;
dp[i]=(dp[i]-dp[j]*C(x+y-u-v,x-u)%mod)%mod;
if(dp[i]<0)dp[i]+=mod;
}
}
printf("%I64d\n",dp[n]);
}
return 0;
}