犯了个睿智错误调了30min真是吃**了
首先由于\(Ax\times By-Ay\times Bx\not =0\),那么我们显然可以把两种走法看作基底,每个点都可以表示成两种走法的次数的有序数对
显然这种表示法是唯一的(如果存在的话)
那么原来的问题其实就变成一般的坐标系上走路了,只能向上和向右,不能经过障碍点
然后这个题好像暑假在CX考过,所以才会做的说,我们考虑先把所有关键点(包括终点)按\(x\)排序,这样只能从前面的点走到后面的点而不能走回头
设\(f_i\)表示走到\(i\)点不经过\(i\)之前的关键点的方案数,\(g_{i,j}\)表示从\(i\)走到\(j\)不考虑任何因素的方案数,显然\(g_{i,j}=C_{x_j-x_i+y_j-y_i}^{x_j-x_i}\)
那么我们有:
\[f_{i}=g_{0,i}-\sum_{j=1}^{i-1} g_{j,i}\times f_j\]
不经过的方案=所有方案-必定经过的方案,一个套路的容斥
这也启示我们有的时候我们可以通过定下顺序使得传统的容斥复杂度骤降,也算是一种常用套路吧
#include<cstdio>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=505,mod=1e9+7,S=500000;
int Ax,Ay,Bx,By;
struct data
{
int x,y;
inline bool trans(void)
{
int a1=By*x-Bx*y,a2=Ax*By-Ay*Bx,b1=Ay*x-Ax*y,b2=Bx*Ay-Ax*By;
if (a2==0||b2==0) return 0; if ((a1/a2)*a2!=a1) return 0;
if ((b1/b2)*b2!=b1) return 0; x=a1/a2; y=b1/b2; return 1;
}
friend inline bool operator < (const data& A,const data& B)
{
return A.x==B.x?A.y<B.y:A.x<B.x;
}
}a[N],p,tar; int n,fact[S+5],inv[S+5],f[N];
inline void dec(int& x,CI y)
{
if ((x-=y)<0) x+=mod;
}
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inline void init(CI n)
{
RI i; for (fact[0]=i=1;i<=n;++i) fact[i]=1LL*fact[i-1]*i%mod;
for (inv[n]=quick_pow(fact[n]),i=n-1;~i;--i) inv[i]=1LL*inv[i+1]*(i+1)%mod;
}
inline int C(CI n,CI m)
{
if (n<m) return 0; return 1LL*fact[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
RI i,j; scanf("%d%d%d%d%d%d%d",&tar.x,&tar.y,&n,&Ax,&Ay,&Bx,&By);
if (!tar.trans()) return puts("0"),0; for (i=1;i<=n;++i)
{
scanf("%d%d",&a[i].x,&a[i].y);
if (!a[i].trans()||a[i].x>tar.x||a[i].y>tar.y) --i,--n;
}
for (a[++n]=tar,init(S),sort(a+1,a+n+1),i=1;i<=n;++i)
for (f[i]=C(a[i].x+a[i].y,a[i].x),j=1;j<i;++j)
dec(f[i],1LL*C(a[i].x-a[j].x+a[i].y-a[j].y,a[i].x-a[j].x)*f[j]%mod);
return printf("%d",f[n]),0;
}