zoukankan      html  css  js  c++  java
  • 【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;
    }
  • 相关阅读:
    Bootstrap3 表格-条纹状表格
    Bootstrap3 表格-基本表格
    Bootstrap3 代码-程序输出
    Bootstrap3 代码-变量
    垃圾回收相关(深入理解Java虚拟机中的内容)
    JVM内存管理及GC机制
    关于Spring IOC (DI-依赖注入)需要知道的一切
    关于 Spring AOP (AspectJ) 该知晓的一切
    JDK1.8源码分析之HashMap
    深入理解Java类加载器(ClassLoader)
  • 原文地址:https://www.cnblogs.com/wans-caesar-02111007/p/9786626.html
Copyright © 2011-2022 走看看