ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

骗我呢

2022-08-14 19:04:22  阅读:124  来源: 互联网

标签: end ll 矩阵 端点 return MOD


题意:

求有多少个 \(n × m\) 的矩阵满足

  • \(0\) \(≤\) \(a_i,_j\) \(≤\) \(m\)
  • \(a_{i,j}\) \(<\) \(a_{i,j + 1}\)
  • \(a_{i,j}\) \(<\) \(a_{i-1, j + 1}\)

首先观察到 \(a_{i,j}\) 的取值范围以及矩阵的列,得出矩阵的每一行必然有一个在 \(0\) ~ \(m\) 之间的数是不存在的。

通过第二个矩阵需要满足的条件得出每一行除去这个不存在的数,这个序列是单调递增的。

设第 \(i\) 行这个不存在的数为 \(b_{i}\),那么看看 \(b_i\) 与 \(a_{i, j}\) 的关系。

根据矩阵每一行单调递增的性质,不难得出:

\(a_{i, j}\) \(=\) \(j\) \(-\) \(1\) \(+\) \([\) \(b_i\) \(≤\) \(j\) \(]\);

再将这个式子代入矩阵需要满足的第三个条件得出:

\(j\) \(-\) \(1\) \(+\) \([\) \(b_i\) \(≤\) \(j\) \(]\) \(<\) \(j\) \(+\) \(1\) \(-\) \(1\) \(+\) \([\) \(b_{i - 1}\) \(≤\) \(j\) \(+\) \(1\) \(]\);

化简可得:

\([\) \(b_i\) \(≤\) \(j\) \(]\) \(<\) \(1\) \(+\) \([\) \(b_{i - 1}\) \(≤\) \(j\) \(+\) \(1\) \(]\);

因此可以知道:

当 \(b_i\) \(≤\) \(j\) 成立时 \(b_{i - 1}\) \(≤\) \(j\) \(+\) \(1\) 一定也成立。

进而可得:

\(b_i\) \(≥\) \(b_{i - 1}\) \(-\) \(1\);

得出这个关系后,我们设 \(f_{i, j}\) 表示第 \(i\) 行中值为 \(j\) 的数不存在所能构成的矩阵的个数。

根据上面 \(b_i\) 和 \(b_{i-1}\) 的关系式,不难得出 \(f_{i,j}\) 的转移方程:

\(f_{i,j}\) \(=\) \(\sum_{k = 0}^{j + 1}\) \(f_{i-1, k}\);

将这个方程展开化简得出:

\(f_{i, j}\) \(=\) \(f_{i, j - 1}\) \(+\) \(f_{i - 1, j + 1}\);

边界条件:

\(f_{i, 0}\) \(=\) \(f_{i - 1, 0}\) \(+\) \(f_{i - 1, 1}\);

根据上面的最开始得出的转移方程可以知道最终答案就是 \(f_{n + 1, m - 1}\)

将转移式子转化为一张图可得:

图1

发现图的左边与中间并不整齐,因此再转化:

图2

因此此题此时转化为了从左上角的 \((0,\) \(-1)\) 号点,每次可以向左下或者向右走,不能走出图的边界,走到画绿挑的点 \((2,\) \(2)\) 号点(到蓝挑点也一样)的方案数。

首先只有向左下走一种方法可以到达下一层,而我们一共需要下 \(n\) 层,因此向左下走的步数就为 \(n\),且这样走一定不会越出矩阵下面或上面的边界。

此时也相当于我们一共向左也走了 \(n\) 步,而为了到达右端的 \(m\) \(-\) \(1\) 点,我们还需要向右走 \(n\) \(+\) \(m\) 步,因此问题再次转化为:

在一个数轴上,从 \(-1\) 出发,向左走 \(n\) 步,向右走 \(n\) \(+\) \(m\) 步,不越过 \([\) \(-1,\) \(m\) \(]\) 这个范围,求方案数。

为了方便,将整体向右平移一位,问题最终变为:

在一个数轴上,从原点出发,向左走 \(n\) 步,向右走 \(n\) \(+\) \(m\) 步,不越过 \([\) \(0,\) \(m\) \(+\) \(1\) \(]\) 这个范围,求方案数。

考虑采用反射的方法,以点 \(-1\) 为中心对称,以 \(m\) \(+\) \(2\) 为中心进行对称,而这个方法的基础可以在 这道题 学习。

相较于普通的采用反射法的题来说,此题最大的不同就是需要进行反复容斥,即我们不仅需要求出越过原点的方案数,还需要求出先越过 \(m\) \(+\) \(1\) 再越过原点的方案数,再就还需要求出先越过原点再越过 \(m\) \(+\) \(1\) 再越过原点的方案数,因此可以递归求解。

以先横跨左端点为例:

设 \(crossL(l,\) \(r,\) \(end)\) 表示当前经过若干次反射得到的限制区间为 \(l\) ~ \(r\),需要到达的终点为 \(end\),的方案数。

那么它的返回值就是从 \(0\) 到 \(end\) 走 \(2n\) \(+\) \(m\) 步的方案数 \(-\) 再跨一遍左(右)区间的方案数。

设向左走 \(a\) 步,向右走 \(b\) 步到达 \(end\)(这里 \(a\),\(b\) 是啥都无所谓)

那么可得方程:

\(\begin{cases}a+b=2n + m\\ a-b=end\\ \end{cases}\)

解得

\(a\) \(=\) \(n\) \(+\) \(\frac{m + end}{2}\);

所以总的方案数就为 \(C(n\) \(+\) \(\frac{m + end}{2},\) \(2n\) \(+\) \(m)\);

当然还需要减去 \(crossL(2\) \(\times\) \((l\) \(-\) \(1)\) \(-\) \(r,\) \(2\) \(\times\) \((l\) \(-\) \(1)\) \(-\) \(l,\) \(2\) \(\times\) \((l\) \(-\) \(1)\) \(-\) \(end)\)。这是因为对于每一次反射,区间左端点就变为了右端点,右端点就变为了左端点,因此我们只需不断的按照当前的左端点进行反射即可。

具体见 code:

#include <cstdio>
#include <cmath>

typedef long long ll;

const int MAXN = 1e6;
const ll MOD = 1e9 + 7;

ll n, m;
ll fac[3 * MAXN + 10], invFac[3 * MAXN + 10];

ll quickPow(ll x, ll k) {

	ll ans = 1ll;
	while(k) {
		if(k & 1) {
			ans = ans * x % MOD;
		}
		k >>= 1;
		x = x * x % MOD;
	}

	return ans;
}

ll C(ll mm, ll nn) {
	return fac[nn] * invFac[mm] % MOD * invFac[nn - mm] % MOD;
}

ll crossR(ll l, ll r, ll end) {
	if(abs(end) > 2 * n + m) return 0ll; //判断终点能否到达 
	return (C(n + ((m + end) >> 1), 2 * n + m) - crossR(2 * (r + 1) - r, 2 * (r + 1) - l, 2 * (r + 1) - end)) % MOD;
}

ll crossL(ll l, ll r, ll end) {
	if(abs(end) > 2 * n + m) return 0ll;
	return (C(n + ((m + end) >> 1), 2 * n + m) - crossL(2 * (l - 1) - r, 2 * (l - 1) - l, 2 * (l - 1) - end)) % MOD;
}

int main() {

	scanf("%lld %lld", &n, &m);
	
	fac[0] = 1ll, invFac[0] = 1ll;
	fac[1] = 1ll, invFac[1] = 1ll;
	for(int i = 2; i <= 2 * n + m; ++i) { //求阶乘和其逆元,以便于 O(1) 计算组合数 
		fac[i] = fac[i - 1] * i % MOD;
		invFac[i] = quickPow(fac[i], MOD - 2);
	}
		
	ll ans = ((crossR(0, m + 1, m) + crossL(0, m + 1, m)) % MOD - C(n + m, 2 * n + m)) % MOD; //第一次横跨L和R把中间这一段算了两遍,因此要再减一下 
	
	printf("%lld\n", (ans + MOD) % MOD);

	return 0;
}

标签:,end,ll,矩阵,端点,return,MOD
来源: https://www.cnblogs.com/louis660/p/16586016.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有