4767: 两双手

4767: 两双手

Time Limit: 10 Sec   Memory Limit: 256 MB
Submit: 581   Solved: 167
[ 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

HINT

Source

[ Submit][ Status][ Discuss]



显然,能到达的每个点,到达时走到的步数是唯一确定的

相当于解一个二元一次方程组

那么按照起点出发经过的步数,将所有被禁止的点排序

定义f[i]:起点走到第i个点,中间不经过其它点的方案数

那么,f[i] = ways(1,i) - ∑f[k]*ways(k,i) (k = 2~i-1)

其中ways(x,y)即从点x走到点y的方案数,用组合数算一下就行了

预处理阶乘和逆元,O(n^2)

注意本题阶乘和逆元范围略大。。。。于是RE了好久

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
#include<cmath>
#include<stack>
using namespace std;
 
const int N = 505;
const int M = 5E5 + 50;
typedef long long LL;
const LL mo = 1000000007;
 
struct Point{
    int x,y,step; Point(){}
    Point(int x,int y,int step): x(x),y(y),step(step){}
    bool operator < (const Point &B) const {return step < B.step;}
}p[N];
 
int n,Ex,Ey,tot,dx[2],dy[2],f[N],Fac[M],Inv[M];
 
inline int Mul(const LL &x,const LL &y) {return x * y % mo;}
inline int Add(const int &x,const int &y) {return x + y < mo ? x + y : x + y - mo;}
inline int Dec(const int &x,const int &y) {return x - y >= 0 ? x - y : x - y + mo;}
inline int C(const int &N,const int &M) {return Mul(Fac[N],Mul(Inv[M],Inv[N - M]));}
 
int ksm(int x,int y)
{
    int ret = 1;
    for (; y; y >>= 1)
    {
        if (y & 1) ret = Mul(ret,x);
        x = Mul(x,x);
    }
    return ret;
}
 
inline int Ways(int x,int y)
{
    int A = x * dy[1] - dx[1] * y;
    int B = dx[0] * dy[1] - dx[1] * dy[0];
    if (A % B != 0) return 0;
    int a = A / B,b;
    if (dx[1] != 0)
    {
        b = x - dx[0] * a;
        if (b % dx[1] != 0) return 0;
        b /= dx[1];
    }
    else
    {
        b = y - dy[0] * a;
        if (b % dy[1] != 0) return 0;
        b /= dy[1];
    }
    return (a < 0 || b < 0) ? 0 : C(a + b,a);
}
 
inline int Calc(int x,int y)
{
    int A = x * dy[1] - dx[1] * y;
    int B = dx[0] * dy[1] - dx[1] * dy[0];
    if (A % B != 0) return -1;
    int a = A / B,b;
    if (dx[1] != 0)
    {
        b = x - dx[0] * a;
        if (b % dx[1] != 0) return -1;
        b /= dx[1];
    }
    else
    {
        b = y - dy[0] * a;
        if (b % dy[1] != 0) return -1;
        b /= dy[1];
    }
    return (a < 0 || b < 0) ? -1 : a + b;
}
 
int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif
     
    Fac[0] = 1; for (int i = 1; i < M; i++) Fac[i] = Mul(Fac[i - 1],i);
    Inv[M - 1] = ksm(Fac[M - 1],mo - 2); for (int i = M - 2; i >= 0; i--) Inv[i] = Mul(Inv[i + 1],i + 1);
    cin >> Ex >> Ey >> n >> dx[0] >> dy[0] >> dx[1] >> dy[1];
    int End = Calc(Ex,Ey);
    if (End == -1) {cout << 0 << endl; return 0;}
    p[++tot] = Point(0,0,0); p[++tot] = Point(Ex,Ey,End);
    for (int i = 1; i <= n; i++)
    {
        int x,y; scanf("%d%d",&x,&y);
        int now = Calc(x,y);
        if (now == -1 || now >= End) continue;
        p[++tot] = Point(x,y,now);
    }
    sort(p + 1,p + tot + 1); f[1] = 1;
    for (int i = 2; i <= tot; i++)
    {
        f[i] = Ways(p[i].x,p[i].y);
        for (int j = 2; j < i; j++)
            f[i] = Dec(f[i],Mul(f[j],Ways(p[i].x - p[j].x,p[i].y - p[j].y)));
    }
    cout << f[tot] << endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值