題意:
棋盤上的一個棋子,給出他的兩種移動方式:
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]);
}