bzoj4767两双手 容斥+组合

Posted wsy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj4767两双手 容斥+组合相关的知识,希望对你有一定的参考价值。

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两双手 容斥+组合的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 4767 两双手

[BZOJ 4767]两双手(组合数学+Dp)

[BZOJ4767]两双手

Bzoj4767 两双手

bzoj4767 两双手

BZOJ[4767] 两双手