10.21 路径组计数

题意

给定一个 (N imes M) 的矩阵,定义一次移动为向右移动一个单位或向上移动一个单位

你要从 ((1,1)) 移动至 ((N,M)), 且要走两次,我们称这两次的路线分别为 (A,B)

我们称一个 ((A,B)) 二元组是合法的,则 (A,B) 除了在 ((1,1),(N,M)) 以外,其它经过的格子都不相同

请你统计所有合法二元组的数量。注意 ((A,B))((B,A)) 为相同的二元组


解法

首先有一个 naive 的想法:既然路线只能在 ((1,1),(N,M)) 重复,那么两条路线的起点和终点一定分别是((1,2),(2,1))((N-1,M),(N,M-1))

(calc(N,M))((1,1))((N,M)) 的路径条数,这个可以预处理组合数后 (O(1)) 计算出来

那么答案即为 (calc(N-1,M-1)^2)

很容易发现这样计算是错误的,因为这样还是会有路线重复的二元组

仔细观察就可以发现,任意一个按照上面方式计算的不合法的二元组(即两条路线在某处有交集)

它们一定唯一对应一组起点和终点分别为 ((1,2),(2,1))((N,M-1),(N-1,M)) 的路径

因此答案最后为 (calc(N-1,M-1)^2 - calc(N-2,M) - calc(N,M-2))


代码

#include <cstdio>

using namespace std;

const int MAX_N = 2e6 + 10;
const int mod = 998244353;

int T, N, M;

int fac[MAX_N], Ifac[MAX_N];

inline void inc(int& x, int y) { (x += y) >= mod ? x -= mod : 0; }
inline int mul(int x, int y) { return 1LL * x * y % mod; }
inline int sqr(int x) { return 1LL * x * x % mod; }

int fsp(int x, int y = mod - 2) {
	int res = 1;
	for (; y; x = sqr(x), y >>= 1)  if (y & 1)  res = mul(res, x);
	return res;
}

void init() {
	int lim = 2e6;
	fac[0] = 1;
	for (int i = 1; i <= lim; ++i)  fac[i] = mul(fac[i - 1], i);
	Ifac[lim] = fsp(fac[lim]);
	for (int i = lim - 1; i >= 0; --i)  Ifac[i] = mul(Ifac[i + 1], i + 1);
}

int C(int n, int m) { return mul(fac[n], mul(Ifac[m], Ifac[n - m])); }

int calc(int n, int m) { return C(n + m - 2, n - 1); }

int main() {
	
	init();
	
	scanf("%d", &T);
	
	while (T--) {
		scanf("%d%d", &N, &M);
		printf("%d
", (mod + sqr(calc(N - 1, M - 1)) - mul(calc(N, M - 2), calc(N - 2, M))) % mod);
	}
		
	return 0;
}

原文地址:https://www.cnblogs.com/VeniVidiVici/p/11747666.html