题意:
棋盘上的一个棋子,给出他的两种移动方式:
1.
2.
现给出一些不能走的障碍点n个,求(0,0)到(Ex,Ey)的方案数
Solution:
因为题目保证了 ,所以从原点出发走到某个点所需的两种走法的次数是唯一的
具体可以列出方程,设到点(X,Y)分别需要a,b步
我们只要把指定点的a,b算出来后,问题就转化为一个只能往右或者往上的常规路径计数问题
但是(Ex,Ey)所对应的a,b可能很大,所以我们不能递推地算
考虑容斥:
我们知道如果没有障碍的话从 到 的方案数为
设 为从原点出发到达第i个关键点的方案数, 表示从i到j的方案数
那么我们可以得到:
显然g数组就是一个组合数,预处理阶乘以及阶乘的逆元即可
复杂度
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int mod=1e9+7;
const int N=1000000;
int n,m,t,cnt;
int f[510];
int a,b,c,d,mi[1000010],inv[1000010];
struct P{
int x,y;
}p[510];
bool calc(int &x,int &y)
{
int s1=x*d-y*c,t1=a*d-b*c,s2=x*b-y*a,t2=c*b-d*a;
if (t1==0||t2==0) return 0;
if (s1%t1) return 0;else x=s1/t1;
if (s2%t2) return 0;else y=s2/t2;
return 1;
}
int fast_pow(int x,int a)
{
int ans=1;
for (;a;a>>=1,x=1ll*x*x%mod)
{
if (a&1) ans=1ll*ans*x%mod;
}
return ans;
}
bool cmp(P a,P b){if(a.x==b.x) return a.y<b.y; return a.x<b.x;}
int C(int n,int m){if (n<m) return 0;return 1ll*mi[n]*inv[m]%mod*inv[n-m]%mod;}
int main()
{
scanf("%d%d%d",&n,&m,&t);
scanf("%d%d%d%d",&a,&b,&c,&d);
if ((!calc(n,m))||n<0||m<0) {printf("0\n");return 0;}
for (int x,y,i=1;i<=t;i++)
{
scanf("%d%d",&x,&y);
if (calc(x,y)&&x>=0&&y>=0&&x<=n&&y<=m) p[++cnt]={x,y};
}
mi[0]=1;
for (int i=1;i<=N;i++) mi[i]=1ll*mi[i-1]*i%mod;
inv[N]=fast_pow(mi[N],mod-2);
for (int i=N-1;i>=0;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod;
p[++cnt]={n,m};
sort(p+1,p+1+cnt,cmp);
f[0]=1;
for (int i=1;i<=cnt;i++)
{
f[i]=C(p[i].x+p[i].y,p[i].x);
for (int j=1;j<i;j++)
f[i]=(1ll*f[i]+mod-(1ll*C(p[i].x-p[j].x+p[i].y-p[j].y,p[i].x-p[j].x)*f[j]%mod))%mod;
}
printf("%d",f[cnt]);
}