当前位置: 首页 > news >正文

Codeforces 559C 详细题解

文章目录

      • 1. 题意
      • 2. 题解
        • 2.1 情况0:如果不存在障碍物
        • 2.2 情况1: 如果存在一个障碍物
        • 3.3 情况2:如果存在两个障碍物
        • 3.4 情况3: 如果存在多个障碍物
      • 4. 代码实现
      • 5. 从集合角度看
      • 6. 参考

1. 题意

  给定 h × w h\times w h×w大小的二维数组, 再给定 n n n个障碍

物。我们从左上角出发,每次只能向右或者向下走

动,问有多少种方法从左上到右下?

cf559c

2. 题解

动态规划+组合计数+容斥原理+乘法逆元

我们一步步慢慢分析

2.1 情况0:如果不存在障碍物

  首先我们分析,如果没有障碍物,

( 0 , 0 ) (0,0) (0,0) ( x , y ) (x,y) (x,y)一共有多少种走法呢?没错应该是

( x + y x ) {x+y \choose x} (xx+y)种。为什么呢?我们一共要走

x + y x+y x+y步, x x x步向下, y y y步向右,因此也就是 x + y x+y x+y

步中选择 x x x步向下,或者 y y y步向下, 也就是

a n s = ( x + y x ) = ( x + y y ) ans ={x+y \choose x}={x+y \choose y} ans=(xx+y)=(yx+y)

2.2 情况1: 如果存在一个障碍物

  如果在 ( 0 , 0 ) (0,0) (0,0) ( x , y ) (x,y) (x,y)之间存在一个障碍物

( x 0 , y 0 ) (x_0,y_0) (x0,y0), 我们可以直接用总数减去通过该障碍物的

路径数目。经过障碍物到达右下的路径数可以通过

乘法计数来求得

( x 0 + y 0 x 0 ) ( x + y − x 0 − y 0 x − x 0 ) {x_0+y_0 \choose x_0}{x+y-x_0-y_0 \choose x-x_0} (x0x0+y0)(xx0x+yx0y0)

因此最终的路径数目为
a n s = ( x + y x ) − ( x 0 + y 0 x 0 ) ( x + y − x 0 − y 0 x − x 0 ) ans={x+y \choose x}-\\{x_0+y_0 \choose x_0}{x+y-x_0-y_0 \choose x-x_0} ans=(xx+y)(x0x0+y0)(xx0x+yx0y0)

3.3 情况2:如果存在两个障碍物

  假设这两个障碍物位置为

B 0 ( x 0 , y 0 ) , B 1 ( x 1 , y 1 ) B_0(x_0, y_0), B_1(x_1, y_1) B0(x0,y0),B1(x1,y1)

  我们需要分情况进行讨论,这两个障碍物是否

独立。所谓独立就是说,在经过 B 0 B_0 B0去往右下角终点

的路径上是否会出现点 B 1 B_1 B1; 如果出现了,那么在计

数的时候就会出现重复,需要去除重复的路径数。

我们容易得到,如果两者之间只要满足

( x 0 − x 1 ) ( y 0 − y 1 ) < 0 (x_0-x_1)(y_0-y_1) < 0 (x0x1)(y0y1)<0, 它们两点之间就独立。

  对于独立的点,我们逐个减去经过它到达终点

的方案数就可以了。

a n s = ( x + y x ) − ∑ i = 0 1 ( x i + y i x i ) ( x − x i + y − y i x − x i ) ans= {x+y \choose x} -\\\sum_{i=0}^1{x_i+y_i \choose x_i} { x-x_i+y-y_i \choose x-x_i} ans=(xx+y)i=01(xixi+yi)(xxixxi+yyi)


  而对于两点不独立存在重复的情况,我们要试

着让他们独立。

  假设 B 1 在 B 0 B_1在B_0 B1B0的右下,也就是 x 0 < = x 1 ∧ y 0 < = y 1 x_0 <= x_1 \land y_0 <= y_1 x0<=x1y0<=y1

  我们在计算经过 B 0 B_0 B0到达右下角的路径中必然包

含了同时经过 B 0 B 1 B_0B_1 B0B1到达右下角的路径,因此我们

只需要计算经过 B 1 B_1 B1而经过 B 0 B_0 B0到达右下角的路径数

目。也就是到达 B 1 B_1 B1的路径上不能有其他障碍物,而

从左上角不经过障碍物到 B 1 B_1 B1的方案数实际上就是求

情况1

我们定义 d p [ i ] [ j ] dp[i][j] dp[i][j]为不经过障碍物从 ( 0 , 0 ) (0, 0) (0,0)到达

( i , j ) (i,j) (i,j)的可能路径数。最终的路径数为:

a n s = d p [ x ] [ y ] = ( x + y x ) − ∑ i = 0 1 d p [ x i ] [ y i ] ( x + y − x i − y i x − x i ) ans = dp[x][y]= {x+y \choose x}-\\\sum_{i=0}^1dp[x_i][y_i]{x+y-x_i -y_i \choose x-x_i} ans=dp[x][y]=(xx+y)i=01dp[xi][yi](xxix+yxiyi)

3.4 情况3: 如果存在多个障碍物

  多个障碍物独立的情况仍然是直接减就可以了
a n s = d p [ x ] [ y ] = ( x + y x ) − ∑ i = 0 k ( x i + y i x i ) ( x − x i + y − y i x − x i ) ans=dp[x][y]={x+y \choose x} -\\\sum_{i=0}^k{x_i+y_i \choose x_i} { x-x_i+y-y_i \choose x-x_i} ans=dp[x][y]=(xx+y)i=0k(xixi+yi)(xxixxi+yyi)
  如果这 k k k个点都不独立,我们需要从左

上的点开始算,因为它会包含经过后面障碍的路

径。依次将它们独立,也就是依次求出 d p [ x i ] [ y i ] dp[x_i][y_i] dp[xi][yi]

再像处理情况2那样处理最终的路径数

a n s = d p [ x ] [ y ] = ( x + y x ) − ∑ i = 0 k d p [ x i ] [ y i ] ( x + y − x i − y i x − x i ) ans=dp[x][y]= {x+y \choose x}-\\\sum_{i=0}^kdp[x_i][y_i]{x+y-x_i -y_i \choose x-x_i} ans=dp[x][y]=(xx+y)i=0kdp[xi][yi](xxix+yxiyi)

4. 代码实现

  我们将右下角坐标也放入障碍物数组,再

将障碍物按左上右下的顺序进行排序;依次求

d p [ x i ] [ y i ] dp[x_i][y_i] dp[xi][yi]即可;组合数较大需要取模,预处理乘

法逆元就可以了, 可以用费马小+快速幂或者是扩展

欧几里得求出最大的阶乘逆元,再线性递推求出其

他阶乘的逆元。

#include <iostream>
#include <algorithm>
#include <vector>
#include <utility>

using ll = long long;

static constexpr int MAXN = 200005;

ll fac[MAXN + 1];
ll ifac[MAXN + 1];
ll MOD = 1e9 + 7;


ll exgcd(ll a, ll b, ll &x, ll &y) {
    if (0 == b) {
        x = 1;
        y = 0;
        return a;
    }
    ll d = exgcd( b, a % b, y, x);

    y -= (a / b) * x;

    return d;
}
ll fpow(ll base, ll cnt) {

    ll ans = 1;
    ll tmp = base;

    while (cnt) {
        if (cnt & 1) ans = ans * tmp % MOD;
        tmp = tmp * tmp % MOD;
        cnt >>= 1;
    }

    return ans;
}


void init () {

    fac[0] = ifac[0] = 1;
    fac[1] = 1;
    ifac[1] = 1;
    for (int i = 2; i <= MAXN; i++) {
        fac[i] = fac[i - 1] * i % MOD; 
    }

    //ll y;
    //exgcd( fac[MAXN], MOD, ifac[MAXN], y);
    
    ifac[MAXN] = fpow(fac[MAXN], MOD - 2);


    for (int i = MAXN - 1; i > 1; i--) {
        ifac[i] = ifac[i + 1] * (i + 1) % MOD;
    }

}
ll Comb(int n, int k) {
    if (n == k || k == 0) {
        return 1;
    }
    return fac[n] * ifac[k] % MOD * ifac[n - k] % MOD;
}
int main()
{


    init();
    int h;
    int w;
    int n;


    std::cin >> h >> w >> n;
    std::vector<std::pair<int, int> > b;


    for (int i = 0;i < n;i++) {
        int x;
        int y;
        std::cin >> x >> y;
        b.push_back(std::make_pair<>(x, y));
    }
    std::sort(b.begin(), b.end(),[](const std::pair<int,int> &f, const std::pair<int,int> &s){
        return f.first == s.first ? f.second < s.second : f.first < s.first;
    });


    b.push_back(std::make_pair( h, w));

    std::vector<ll> dp( n + 1, 0);


    for (int i = 0;i < n + 1; ++i) {
        int r = b[i].first;
        int c = b[i].second;

        ll res = Comb(r + c - 2, r - 1);
        for (int j = 0;j < i;++j) {
            int rr = b[j].first;
            int cc = b[j].second;
            if (rr <= r && cc <= c) {
                res = res + MOD -  (dp[j] * Comb(r + c - rr - cc, r - rr) % MOD) ;
                res = res % MOD ;
            }
        }

        dp[i] = res;
    }

   
   
    std::cout << dp[n] << std::endl;


    return 0;
}


5. 从集合角度看

假设共有 n n n个障碍物分别为 B 0 ( x 0 , y 0 ) ⋯ B n − 1 ( x n − 1 , y n − 1 ) B_0(x_0,y_0)\cdots B_{n-1}(x_{n-1},y_{n-1}) B0(x0,y0)Bn1(xn1,yn1)

且当 i < j , x i ≤ x j ∧ y i ≤ y j i <j, x_{i} \le x_{j} \land y_i \le y_j i<j,xixjyiyj

也就是上面说得排好序,经过 B i B_i Bi到达终点的路径

包含同时经过 B i   B j B_i\ B_j Bi Bj的路径。

我们令事件 P i , 0 ≤ 0 < n P_i, 0 \le 0 <n Pi,00<n为经过 P i P_i Pi到达终点的路

径。

d p [ i ] dp[i] dp[i]为从 ( 0 , 0 ) (0,0) (0,0)不经过障碍物到达 B i B_i Bi的路径

数。

那么实际上 d p [ i ] dp[i] dp[i]

d p [ i ] = C n t ( P 0 ⋯ P i − 1 ‾ P i ) dp[i] =Cnt(\overline{P_0 \cdots P_{i-1}}P_i) dp[i]=Cnt(P0Pi1Pi)

求出所有不经过障碍物到达右下的路径相当于:

P 0 P 1 ⋯ P n − 1 ‾ = 1 − ( P 0 + P 0 ‾ P 1 + ⋯ P 0 P 1 ⋯ P n − 2 ‾ P n − 1 ) \overline{P_0P_1\cdots P_{n-1}}=1 -(P_0+ \overline{P_0}P_1+\\\cdots \overline{P_0P_1\cdots P_{n-2}}P_{n-1}) P0P1Pn1=1(P0+P0P1+P0P1Pn2Pn1)

6. 参考

03xf-sarinee

相关文章:

  • 深入解析C++函数指针与指针函数:从原理到实战
  • 无法打开包括文件: “crtdbg.h”: No such file or directory
  • 【Blender】二、建模篇--06,曲线建模/父子级和蒙皮修改器
  • 《CentOS 7 镜像源失效终极解决方案(2024年更新)》——生命周期终止后的镜像修复与替代方案
  • Linux编程----filelo(2)
  • 【大模型LLM】DeepSeek LLM Scaling Open-Source Language Models with Longtermism
  • pandas Dataframe介绍以及创建
  • 使用 Python 调用 Ollama API 并调用 deepseek-r1:8b 模型
  • 【量化科普】Liquidity,流动性
  • 鸟语林-论坛系统自动化测试
  • 【网络安全 | 漏洞挖掘】账户接管+PII+原漏洞绕过
  • 【语音科学计算器】当前汇率
  • Python简单使用MinerU
  • 学习Linux准备2
  • 深入理解设计模式之解释器模式
  • 如何设计app测试用例
  • Docker下的Elastic search
  • 使用 LS-DYNA 光滑粒子流体动力学 (SPH) 进行搅拌摩擦焊接
  • 【CXX】3 其它Rust与C++互操作工具详解
  • 网页请求腾讯云环境的云函数
  • 央行将增加3000亿元科技创新和技术改造再贷款额度
  • 李云泽:将加快出台与房地产发展新模式相适配的系列融资制度
  • 经济日报:落实落细更加积极的财政政策
  • 上海虹桥机场至北京首都机场快线试运行跨航司自愿签转服务
  • 宋涛就许历农逝世向其家属致唁电
  • 新华社:让历史照鉴未来