• 【BZOJ】4767: 两双手【组合数学】【容斥】【DP】


    4767: 两双手

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

    Solution

    看起来很像一般的网格图求路径数,在没有障碍物的情况下,处理出到达终点分别用两种步数走的步数(解方程唯一固定,再用组合数学,方案数就等于$C(x+y,x)$,$x$和$y$分别是两种步数的次数。

    可是这道题有障碍格。

    所以在上述基础上进行容斥DP即可。将能走到的所有障碍点的$x$和$y$(从原点出发)预处理出来,(将终点的$x$、$y$值也放入结构体)排序后,后面的DP值要容斥减去前面能到达它的方案数。最后答案就是$dp[n]$。(因为一开始保证了结构体里所有障碍点都比目标点要小)

    【注意】预处理阶乘的逆元线性从后往前线性求得,不然会TLE!

    Code

    #include<bits/stdc++.h>
    #define mod 1000000007
    #define LL long long
    using namespace std;
    
    int Ax, Ay, Bx, By, n, Ex, Ey;
    
    struct Node {
        int x, y;
    } QAQ[1005];
    bool cmp(Node a, Node b) { if(a.x == b.x)    return a.y < b.y; return a.x < b.x; }
    
    LL mpow(LL a, LL b) {
        LL ans = 1;
        for(; b; b >>= 1, a = a * a % mod)
            if(b & 1)    ans = ans * a % mod;
        return ans;
    }
    
    void cal(int &x, int &y) {////解方程计算两种步数
        LL a1, a2, b1, b2;
        b1 = y * Ax - x * Ay, b2 = Ax * By - Ay * Bx;
        a1 = x * By - y * Bx, a2 = Ax * By - Ay * Bx;
        if(a2 == 0 || b2 == 0) { x = -1, y = -1; return ; }
        if((a1 / a2) * a2 != a1 || (b1 / b2) * b2 != b1) { x = -1, y = -1; return ; }
        x = a1 / a2, y = b1 / b2;
    }
    
    LL fac[1000005], inv[1000005];
    LL C(LL a, LL b) {
        if(a < b)    return 0;
        return fac[a] * inv[a-b] % mod * inv[b] % mod;
    } 
    
    void init() {
        fac[0] = 1;
        for(int i = 1; i <= 1000000; i ++)
            fac[i] = fac[i-1] * i % mod;
        inv[0] = 1; inv[1000000] = mpow(fac[1000000], mod - 2);
        for(int i = 999999; i >= 1; i --)
            inv[i] = inv[i + 1] * (i + 1) % mod;////线性求阶乘逆元
    }
    
    LL f[1005];
    int main() {
        scanf("%d%d%d", &Ex, &Ey, &n);
        scanf("%d%d%d%d", &Ax, &Ay, &Bx, &By);
        cal(Ex, Ey);
        for(int i = 1; i <= n; i ++) {
            scanf("%d%d", &QAQ[i].x, &QAQ[i].y);
            cal(QAQ[i].x, QAQ[i].y);
            if(QAQ[i].x < 0 || QAQ[i].y < 0 || QAQ[i].x > Ex || QAQ[i].y > Ey) {/////不合法的步数筛掉
                n --; i --;
            }
        }
        QAQ[++n].x = Ex, QAQ[n].y = Ey;
        sort(QAQ + 1, QAQ + 1 + n, cmp);
        
        init();
        
        for(int i = 1; i <= n; i ++) {
            f[i] = C(QAQ[i].x + QAQ[i].y, QAQ[i].x);
            if(f[i] == 0)    continue;
            for(int j = 1; j < i; j ++) {
                f[i] -= (f[j] * C(QAQ[i].x - QAQ[j].x + QAQ[i].y - QAQ[j].y, QAQ[i].x - QAQ[j].x)) % mod;/////容斥
                f[i] = (f[i] % mod + mod) % mod;
            }
        }
        printf("%lld", f[n]);
        return 0;
    }
  • 相关阅读:
    SCILAB简介[z]
    UG OPEN API编程基础 2约定及编程初步
    Office 2003与Office 2010不能共存的解决方案
    UG OPEN API 编程基础 3用户界面接口
    NewtonRaphson method
    UG OPEN API编程基础 13MenuScript应用
    UG OPEN API编程基础 14API、UIStyler及MenuScript联合开发
    UG OPEN API编程基础 4部件文件的相关操作
    UG OPEN API编程基础 1概述
    16 UG Open的MFC应用
  • 原文地址:https://www.cnblogs.com/wans-caesar-02111007/p/9786626.html
Copyright © 2020-2023  润新知