1. 程式人生 > >bzoj4767兩雙手 容斥+組合

bzoj4767兩雙手 容斥+組合

禁止 nbsp 化簡 == space left padding 不能 www.

4767: 兩雙手

Time Limit: 10 Sec Memory Limit: 256 MB
Submit: 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; }

bzoj4767兩雙手 容斥+組合