【7.11】模拟赛-计数

题目链接在这里插入图片描述

计数(count)

【题目描述】
Alice 和 Bob 在平面直角坐标系中下棋。 Alice 的棋子初始时在(0, 0)位置, 要走到(a, b)位
置; Bob 的棋子初始时在(c, 0)位置, 要走到(a, d)位置。棋子只能沿 x 轴或 y 轴正方向移动若
干个单位长度, 问有多少种移动方案使两颗棋子的移动路径不相交。
【输入数据】
输入一行 4 个正整数, 依次为 a, b, c, d。
【输出数据】
输出总方案数对质数 100000007 取模的结果。
【样例输入】
3 2 1 1
【样例输出】
6
【样例解释】
A 走(0,0) → (0,2) → (3,2) 时, B 有 3 种走法:
(1,0) → (1,1) → (3,1)
(1,0) → (2,0) → (2,1) → (3,1)
(1,0) → (3,0) → (3,1)
A 走(0,0) → (1,0) → (1,2) → (3,2)时, B 有 2 种走法。
A 走(0,0) → (1,0) → (1,1) → (2,1) → (2,2) → (3,2)时, B 有 1 种走法。
【数据范围】
对于 50%的数据, a + b <= 20。
对于 70%的数据, a + b <= 2e4。
对于 100%的数据, a + b <= 2e5 且 a > c, b > d

暴力我们两个dfs,当找到一个a路径方案时就dfs出c的所有方案,然后累加即可。

对于这道题我们可以发现A,B在同一条直线上,且A始终在B的上方。
如果我们不考虑重叠的情况,到(a,b)的方案数为 ( a + b a ) {a+b \choose a} (aa+b)

ps:方案数后面的为美式组合数的写法,带C的是苏式的,(我没找到打出来苏式的方法),苏式和美式的上下表示的意思是反的。 ( a b ) {a\choose b} (ba)这个指在a中选b个

到(a,d)的方案数为 ( a − c + b a − c ) {a-c+b\choose a-c} (acac+b);

那么我们考虑减去相交的方案数

那相交的情况怎么处理呢?

如果两个路径相交,必然会有 ≥ 1 ≥1 1的交点,同时,如果我们交换两个路径的最后一个点的后面的路径
那么新生成的两个路径就变成从原点到(a, d),从(c,0)到(a,b);

那么我们只要求出到这两个的方案数,就是相交的方案数。
关于证明:首先,如果原点目标改为(a,d),那么它的路径上的y一定不会高于d;
从(c,0)点到(a,b)的y一定会竖跨整个0 - b的y坐标,即每个点的y坐标能覆盖0-b;
而从原点开始的路径又会覆盖整个0-a的x坐标,因此他们一定相交。
又因为c路径的x不小于c,a路径的y不大于d,因此他们的交点一定出现在 ( c , 0 ) − ( a , d ) (c,0)-(a,d) (c,0)(a,d)的矩阵中。
同时通过上面的路径交换我们可以发现,这两个相交的路径可以通过变换变成符题目目标要求的道路。
而所有相交的又可以反推出是更改目标的路径,因此 ( a + d a ) a + d\choose a (aa+d) * ( a − c + b a − c ) a - c + b\choose a-c (acac+b)为所有x相交的方案数
然后我们拿 ( a + b a ) ∗ ( a − c + d a − c ) − ( a + d a ) ∗ ( a − c + b a − c ) {a + b\choose a} * {a - c + d\choose a-c} - {a+d\choose a} * {a-c+b\choose a-c} (aa+b)(acac+d)(aa+d)(acac+b)就是所有合法的方案数

写这道题我学会的主要还是数论上的知识:关于逆元的知识,快速幂求逆元,线性递推求逆元

#include<bits/stdc++.h>

#define MAXN 200005
#define ll long long

using namespace std;

const ll mod = 1e8 + 7;

inline void read(int &s){
	s = 0;int w = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){ if(ch == '-') w = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s*10 + ch -'0', ch = getchar();
	s *= w;
}

int a, b, c, d;
ll ans = 0;
ll fr[MAXN], inv[MAXN];//fr为预处理的阶乘,inv为逆元

inline ll pow(ll x, ll n){//快速幂求逆元
	if(!n) return 1;
	
	ll res = pow(x, n >> 1);
	res = res * res % mod;
	if(n & 1) res = (res * x) % mod;
	return res;
	
}

ll C(int n, int m){//组合数
	return (fr[m] % mod * inv[n]% mod * inv[m - n] % mod) % mod;
}

int main()
{
	read(a), read(b), read(c), read(d);
	
	fr[0] = 1;
	for(int i = 1; i < MAXN; ++i) fr[i] = (fr[i-1]%mod * i%mod) % mod;
	//求最大的阶乘的逆元
	inv[MAXN - 1] = pow(fr[MAXN - 1], mod - 2);
	//下面线性递推阶乘逆元
	for(int i = MAXN - 1; i > 0; i--) inv[i - 1] = inv[i] * i % mod;
	
	ans = (C(a, a + b) * C(a - c, a - c + d))%mod - (C(a, a + d) * C(a - c, a - c + b))%mod;
	if(ans < 0) ans += mod;
	
	printf("%lld\n", ans);
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BIGBIGPPT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值