[日常训练] 藏宝路径

【问题描述】

经过你的帮助,小Y终于得到了藏宝图。
藏宝图是一个m*n的网格。宝藏分散在(m,n)、 (m,q)两个地方。小Y、小Z分别在(0,0)、(p,0)两个位置,他们决定每个人去一个地方找宝藏。小Y去(m,n)地,小Z去(m,q)地,其中,p &lt; &lt; <m,q &lt; &lt; <n。小Y和小Z只能沿着坐标系中整数组成的网格向x轴或者y轴的正方向爬行。
小Y想知道,在两人所走的路径不重叠(两条路径没有相交的地方)的情况有多少种?

【输入格式】

输入一行,依次为m、n、p、q,都是小于100000的正整数。
###【输出格式】
输出一行一个数,表示所有情况的数量 模(mod) 质数100,000,007的结果。

【输入输出样例】

path.in
3 2 1 1

path.out
6

【样例说明】

当小Y走 ( 0 , 0 ) → ( 0 , 2 ) → ( 3 , 2 ) (0,0) \to (0,2) \to (3,2) (0,0)(0,2)(3,2)时,小Z有3种走法: ( 1 , 0 ) → ( 1 , 1 ) → ( 3 , 1 ) (1,0) \to (1,1) \to (3,1) (1,0)(1,1)(3,1) ( 1 , 0 ) → ( 2 , 0 ) → ( 2 , 1 ) → ( 3 , 1 ) (1,0) \to (2,0) \to (2,1) \to (3,1) (1,0)(2,0)(2,1)(3,1) ( 1 , 0 ) → ( 3 , 0 ) → ( 3 , 1 ) (1,0) \to (3,0) \to (3,1) (1,0)(3,0)(3,1)类似的:
小Y还可以走 ( 0 , 0 ) → ( 0 , 1 ) → ( 1 , 1 ) → ( 1 , 2 ) → ( 3 , 2 ) (0,0) \to (0,1) \to (1,1) \to (1,2) \to (3,2) (0,0)(0,1)(1,1)(1,2)(3,2),此时小Z有2种走法。
小Y还可以走 ( 0 , 0 ) → ( 0 , 1 ) → ( 2 , 1 ) → ( 2 , 2 ) → ( 3 , 2 ) (0,0) \to (0,1) \to (2,1) \to (2,2) \to (3,2) (0,0)(0,1)(2,1)(2,2)(3,2),此时小Z有1种走法。
所以,共6种。

【数据规模】

对于20%的数据,m+n小于10。
对于50%的数据,m+n小于20。
对于70%的数据,m+n小于20000。
对于100%的数据,m+n小于200000。

【分析】组合数学 + 费马小定理

  • 首先我们不考虑路径相交的问题,则从 ( 0 , 0 ) (0, 0) (0,0)走到 ( m , n ) (m, n) (m,n)共有 C m + n n C_{m + n}^n Cm+nn C m + n m C_{m + n}^m Cm+nm条路径(两者等价,表示共走 m + n m + n m+n步,选择 n n n步向上走或选择 m m m步向右走的方案数)
  • 接下来我们先算出总的路径数,再减去不合法的相交路径数
  • 一种不合法的相交路径如下图
  • 实际上也就相当于分为从 ( 0 , 0 ) (0, 0) (0,0) ( m , q ) (m, q) (m,q)和从 ( p , 0 ) (p, 0) (p,0) ( m , n ) (m, n) (m,n)两条路径,如下图
    这里写图片描述
  • 则最后的答案为
    C m + n n × C m − p + q q − C m + q q × C m − p + n n C_{m + n}^n \times C_{m - p + q}^q - C_{m + q}^q \times C_{m - p + n}^n Cm+nn×Cmp+qqCm+qq×Cmp+nn
  • 但这里计算组合数 C C C要用到除法,而我们又要对其取模,则又要引出一个概念:费马小定理
  • 设模数为 p p p(要求为素数),则 1 a ( m o d &ThinSpace;&ThinSpace;   p ) = a p − 2 ( m o d &ThinSpace;&ThinSpace;   p ) \frac{1}{a} (\mod\ p) = a^{p - 2} (\mod\ p) a1(mod p)=ap2(mod p)
  • 那么 C n m ( m o d &ThinSpace;&ThinSpace;   p ) = n ! m ! × ( n − m ) ! ( m o d &ThinSpace;&ThinSpace;   p ) C_n^m(\mod\ p) = \frac{n!} {m! \times (n - m)!} (\mod\ p) Cnm(mod p)=m!×(nm)!n!(mod p) = n ! × ( m ! × ( n − m ) ! ) p − 2 ( m o d &ThinSpace;&ThinSpace;   p ) = n! \times (m! \times (n - m)!)^{p - 2} (\mod\ p) =n!×(m!×(nm)!)p2(mod p)
  • 我们就可以预处理出 x ! ( 0 ≤ x ≤ 200 , 000 ) x!(0 \le x \le 200,000) x!(0x200,000),直接用快速幂计算答案即可

【代码】

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;
typedef long long ll;
const ll Mod = 1e8 + 7; 
const int N = 2e5 + 5;
ll f[N]; int n, m, p, q;

inline ll Pow(ll x, ll k)
{
	ll res = 1;
	while (k)
	{
		if (k & 1) (res *= x) %= Mod;
		(x *= x) %= Mod; k >>= 1;
	}
	return res;
}

inline ll C(const int n, const int m)
{
	return f[n] * Pow(f[m] * f[n - m] % Mod, Mod - 2) % Mod;
}

int main()
{
	freopen("path.in", "r", stdin);
	freopen("path.out", "w", stdout);
	f[1] = f[0] = 1; scanf("%d%d%d%d", &m, &n, &p, &q);
	for (int i = 2; i <= n + m; ++i) f[i] = (f[i - 1] * i) % Mod;
	cout << (C(m + n, n) * C(m - p + q, q) % Mod 
	       - C(m + n - p, n) * C(m + q, q) % Mod + Mod) % Mod << endl;
	fclose(stdin); fclose(stdout);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值