bzoj4767两双手 容斥+组合
4767: 两双手
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 684 Solved: 208
[Submit][Status][Discuss]
Description
老W是个棋艺高超的棋手,他最喜欢的棋子是马,更具体地,他更加喜欢马所行走的方式。老W下棋时觉得无聊,便
决定加强马所行走的方式,更具体地,他有两双手,其中一双手能让马从(u,v)移动到(u+Ax,v+Ay)而另一双手能让
马从(u,v)移动到(u+Bx,v+By)。小W看见老W的下棋方式,觉得非常有趣,他开始思考一个问题:假设棋盘是个无限
大的二维平面,一开始马在原点(0,0)上,若用老W的两种方式进行移动,他有多少种不同的移动方法到达点(Ex,Ey
)呢?两种移动方法不同当且仅当移动步数不同或某一步所到达的点不同。老W听了这个问题,觉得还不够有趣,他
在平面上又设立了n个禁止点,表示马不能走到这些点上,现在他们想知道,这种情况下马有多少种不同的移动方
法呢?答案数可能很大,你只要告诉他们答案模(10^9+7)的值就行。
Input
第一行三个整数Ex,Ey,n分别表示马的目标点坐标与禁止点数目。
第二行四个整数Ax,Ay,Bx,By分别表示两种单步移动的方法,保证Ax*By-Ay*Bx≠0
接下来n行每行两个整数Sxi,Syi,表示一个禁止点。
|Ax|,|Ay|,|Bx|,|By| <= 500, 0 <= n,Ex,Ey <= 500
Output
仅一行一个整数,表示所求的答案。
Sample Input
4 4 1
0 1 1 0
2 3
Sample Output
40
组合+容斥
可以发现两种方法走的步数是一定的
因为 AX*x+BX*y=ex AY*x+BY*y=ey唯一解
特判能不能走到此点,并且把坐标化成二元一次方程组的解(x,y)
那么ans=总方案-路上经过禁止点的方案
算路径方案用组合数
(0,0)一次向上或右走一单位,走到(n,m)的方案为C(n+m,m)
再考虑路上经过禁止点的方案
对于每个禁止点,可以算出到达它的方案,再用容斥减去之前已经经过禁止点的方案
对于禁止点i,如果禁止点j可以到达i,那么到达i的方案要减去到达j再到i的方案
由于坐标化简后相当于只向右上走,所以按坐标排序,只有排在它之前的点可能到达它
/* 代码wa了没调出来。 */ #include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #define ll long long #define N 505 #define mod 1000000007 using namespace std; int ex,ey,ax,ay,bx,by,cnt,n,m,num,fac[1005*1005],f[N]; struct node{int x,y;}p[N]; bool check(int x,int y,int &a,int &b){ int t1=x*by-y*bx,t2=ax*by-ay*bx; if(t1%t2)return 0; a=t1/t2; t1=x*ay-y*ax;t2=-t2; if(t1%t2)return 0; b=t1/t2;return 1; } void pre(){ fac[0]=1; for(int i=1;i<=1e6;i++)fac[i]=(1ll*fac[i-1]*i)%mod; } int quick(int a,int b){ int c=1; while(b){ if(b&1)c=(1ll*c*a)%mod; a=(1ll*a*a)%mod;b>>=1; } return c; } int C(int x,int y){ int ans=fac[x]; int d1=quick(fac[y],mod-2); int d2=quick(fac[x-y],mod-2); ans=(1ll*ans*d1)%mod; ans=(1ll*ans*d2)%mod; return ans; } int calc(int x,int y){ if(x<0||y<0)return 0; return C(x+y,y); } bool cmp(node a,node b){return a.x==b.x?a.y<b.y:a.x<b.x;} int main(){ #ifdef wsy freopen("data.in","r",stdin); #else //freopen(".in","r",stdin); //freopen(".out","w",stdout); #endif int A,B; scanf("%d%d%d",&ex,&ey,&num); scanf("%d%d%d%d",&ax,&ay,&bx,&by); if(!check(ex,ey,n,m)){puts("0");return 0;} for(int i=1;i<=num;i++){ int a,b;scanf("%d%d",&a,&b); if(check(a,b,A,B)&&A>=1&&A<=n&&B>=1&&B<=m) p[++cnt].x=A;p[cnt].y=B; } pre(); p[++cnt].x=n;p[cnt].y=m; sort(p+1,p+1+cnt,cmp); for(int i=1;i<=cnt;i++){ f[i]=calc(p[i].x,p[i].y); for(int j=1;j<i;j++) f[i]=(f[i]-(ll)f[j]*calc(p[i].x-p[j].x,p[i].y-p[j].y))%mod; } f[cnt]<0?f[cnt]+=mod:1; cout<<f[cnt]; return 0; }
If you live in the echo,
your heart never beats as loud.
如果你生活在回声里,
你的心跳声永远不会轰鸣作响。