2021牛客暑期多校训练营9 C、Cells

C、Cells

参考过的题解

题目大意

你有一张无穷大的二维矩阵,你的出发点在格点 ( 0 , a i ) (0,a_i) (0,ai),并且保证了出发点横坐标依次递增: a i < a i + 1 a_iai<ai+1

你的目标点分别是 ( 1 , 0 ) , ( 2 , 0 ) . . . ( n , 0 ) (1,0),(2,0)...(n,0) (1,0),(2,0)...(n,0),你有几个出发点就有几个目标点,求从出发点去目标点走过的路径没有任何交点的方案数。

Solution

考点:LGV引理+多项式卷积

这题第一个难点就是要会转换模型,他的方案数本质上就是一个LGV引理的模板。

稍微提一下LGV引理,它讲的就是你有起点 ( A 1 , A 2 . . . A n ) (A_1,A_2...A_n) (A1,A2...An),目标点 ( B 1 , B 2 . . . B n ) (B_1,B_2...B_n) (B1,B2...Bn),那么你从 n n n个起点出发经过完全不相交的边去到 n n n个终点的方案数会等于下面的行列式。

M = ∣ e ( A 1 , B 1 ) e ( A 1 , B 2 ) ⋯ e ( A 1 , B n ) e ( A 2 , B 1 ) e ( A 2 , B 2 ) ⋯ e ( A 2 , B n ) ⋮ ⋮ ⋱ ⋮ e ( A n , B 1 ) e ( A n , B 2 ) ⋯ e ( A n , B n ) ∣ M= \left| \begin{array}{cccc} e(A_1,B_1) & e(A_1,B_2) & \cdots & e(A_1,B_n) \\ e(A_2,B_1) & e(A_2,B_2) & \cdots & e(A_2,B_n) \\ \vdots & \vdots & \ddots & \vdots \\ e(A_n,B_1) & e(A_n,B_2) & \cdots & e(A_n,B_n) \\ \end{array} \right| M=e(A1,B1)e(A2,B1)e(An,B1)e(A1,B2)e(A2,B2)e(An,B2)e(A1,Bn)e(A2,Bn)e(An,Bn)

e ( A 1 , B 1 ) e(A_1,B_1) e(A1,B1)对应的是从 A 1 A_1 A1去往 B 1 B_1 B1的方案数,那么对应到本题,从 a 1 a_1 a1去往 ( 1 , 0 ) (1,0) (1,0)的方案数就是经典过河卒问题,它的方案数应该是 C a 1 + 1 1 C_{a_1+1}^1 Ca1+11

写出本题的行列式:

M = ∣ C a 1 + 1 1 C a 1 + 2 2 ⋯ C a 1 + n n C a 2 + 1 1 C a 2 + 2 2 ⋯ C a 2 + 2 n ⋮ ⋮ ⋱ ⋮ C a n + 1 1 C a n + 2 2 ⋯ C a n + n n ∣ M= \left| \begin{array}{cccc} C_{a_1+1}^{1} & C_{a_1+2}^{2} & \cdots & C_{a_1+n}^{n} \\ C_{a_2+1}^{1} & C_{a_2+2}^{2} & \cdots & C_{a_2+2}^{n} \\ \vdots & \vdots & \ddots & \vdots \\ C_{a_n+1}^{1} & C_{a_n+2}^{2} & \cdots & C_{a_n+n}^{n} \\ \end{array} \right| M=Ca1+11Ca2+11Can+11Ca1+22Ca2+22Can+22Ca1+nnCa2+2nCan+nn

求解 n n n阶方阵的常用做法是高斯消元,但是很明显 O ( n 3 ) O(n^3) O(n3)的做法无法通过此题,我们要对这个有特点的行列式做一些初等变换。

我们把这个方阵里面的组合数用阶乘的方式打开,可以得到下面的行列式。

M = ∣ ( a 1 + 1 ) ! 1 ! a 1 ! ( a 1 + 2 ) ! 2 ! a 1 ! ⋯ ( a 1 + n ) ! n ! a 1 ! ( a 2 + 1 ) ! 1 ! a 2 ! ( a 2 + 2 ) ! 2 ! a 2 ! ⋯ ( a 2 + n ) ! n ! a 2 ! ⋮ ⋮ ⋱ ⋮ ( a n + 1 ) ! 1 ! a n ! ( a n + 2 ) ! 2 ! a n ! ⋯ ( a n + n ) ! n ! a n ! ∣ M= \left| \begin{array}{cccc} \frac{(a_1+1)!}{1!a_1!} & \frac{(a_1+2)!}{2!a_1!} & \cdots & \frac{(a_1+n)!}{n!a_1!} \\ \frac{(a_2+1)!}{1!a_2!} & \frac{(a_2+2)!}{2!a_2!} & \cdots & \frac{(a_2+n)!}{n!a_2!} \\ \vdots & \vdots & \ddots & \vdots \\ \frac{(a_n+1)!}{1!a_n!} & \frac{(a_n+2)!}{2!a_n!} & \cdots & \frac{(a_n+n)!}{n!a_n!} \\ \end{array} \right| M=1!a1!(a1+1)!1!a2!(a2+1)!1!an!(an+1)!2!a1!(a1+2)!2!a2!(a2+2)!2!an!(an+2)!n!a1!(a1+n)!n!a2!(a2+n)!n!an!(an+n)!

我们发现第 i i i列都有 1 i ! \frac{1}{i!} i!1我们把他提出外面去得到:

M = ∏ i = 1 n 1 i ! ∣ ( a 1 + 1 ) ! a 1 ! ( a 1 + 2 ) ! a 1 ! ⋯ ( a 1 + n ) ! a 1 ! ( a 2 + 1 ) ! a 2 ! ( a 2 + 2 ) ! a 2 ! ⋯ ( a 2 + n ) ! a 2 ! ⋮ ⋮ ⋱ ⋮ ( a n + 1 ) ! a n ! ( a n + 2 ) ! a n ! ⋯ ( a n + n ) ! a n ! ∣ M=\prod\limits_{i=1}^n\frac{1}{i!} \left| \begin{array}{cccc} \frac{(a_1+1)!}{a_1!} & \frac{(a_1+2)!}{a_1!} & \cdots & \frac{(a_1+n)!}{a_1!} \\ \frac{(a_2+1)!}{a_2!} & \frac{(a_2+2)!}{a_2!} & \cdots & \frac{(a_2+n)!}{a_2!} \\ \vdots & \vdots & \ddots & \vdots \\ \frac{(a_n+1)!}{a_n!} & \frac{(a_n+2)!}{a_n!} & \cdots & \frac{(a_n+n)!}{a_n!} \\ \end{array} \right| M=i=1ni!1a1!(a1+1)!a2!(a2+1)!an!(an+1)!a1!(a1+2)!a2!(a2+2)!an!(an+2)!a1!(a1+n)!a2!(a2+n)!an!(an+n)!

接下来就要作过一定线性代数的题了,要认出这是一个范德蒙德行列式。

具体我们也能推出来,我们假设取了第一行的前三列,我们可以得出下面的这个行列式。

∣ ( a 1 + 1 ) ( a 1 + 1 ) ∗ ( a 1 + 2 ) ( a 1 + 1 ) ∗ ( a 1 + 2 ) ∗ ( a 1 + 3 ) ∣ \left| \begin{array}{cccc} (a_1+1) & (a_1+1)*(a_1+2) & (a_1+1)*(a_1+2)*(a_1+3) \end{array} \right| (a1+1)(a1+1)(a1+2)(a1+1)(a1+2)(a1+3)

为了好看我们转置一下,并且要知道行列式转置值不变,我们得出下面的这个三行一列行列式,本质上和上面的是一样的。

∣ ( a 1 + 1 ) ( a 1 + 1 ) ∗ ( a 1 + 2 ) ( a 1 + 1 ) ∗ ( a 1 + 2 ) ∗ ( a 1 + 3 ) ∣ \left| \begin{array}{cccc} (a_1+1) \\ (a_1+1)*(a_1+2) \\ (a_1+1)*(a_1+2)*(a_1+3) \end{array} \right| (a1+1)(a1+1)(a1+2)(a1+1)(a1+2)(a1+3)

我们接下来做行初等变换,第二行乘以 − 2 -2 2加到第三行上去,得到

∣ ( a 1 + 1 ) ( a 1 + 1 ) ∗ ( a 1 + 2 ) ( a 1 + 1 ) 2 ∗ ( a 1 + 2 ) ∣ \left| \begin{array}{cccc} (a_1+1) \\ (a_1+1)*(a_1+2) \\ (a_1+1)^2*(a_1+2) \end{array} \right| (a1+1)(a1+1)(a1+2)(a1+1)2(a1+2)

接下来用第一行乘以 − ( a 1 + 1 ) -(a_1+1) (a1+1)加到第三行上去,这样我们就得到

∣ ( a 1 + 1 ) ( a 1 + 1 ) ∗ ( a 1 + 2 ) ( a 1 + 1 ) 3 ∣ \left| \begin{array}{cccc} (a_1+1) \\ (a_1+1)*(a_1+2) \\ (a_1+1)^3 \end{array} \right| (a1+1)(a1+1)(a1+2)(a1+1)3

接着用第一行乘以 − 1 -1 1加到第二行计算到

∣ ( a 1 + 1 ) ( a 1 + 1 ) 2 ( a 1 + 1 ) 3 ∣ \left| \begin{array}{cccc} (a_1+1) \\ (a_1+1)^2 \\ (a_1+1)^3 \end{array} \right| (a1+1)(a1+1)2(a1+1)3

这样一个范德蒙德行列式就出来了,我们再把转置之后的行列式每一列提出一个 ( a i + 1 ) (a_i+1) (ai+1)就变成了标准的范德蒙德行列式。

M = ∏ i = 1 n a i + 1 i ! ∣ 1 1 ⋯ 1 ( a 1 + 1 ) ( a 2 + 1 ) ⋯ ( a n + 1 ) ⋮ ⋮ ⋱ ⋮ ( a 1 + 1 ) n − 1 ( a 2 + 1 ) n − 1 ⋯ ( a n + 1 ) n − 1 ∣ = ∏ i = 1 n ( a i + 1 i ! ∏ 1 ≤ i < j ≤ n ( a j − a i ) ) M=\prod\limits_{i=1}^n\frac{a_i+1}{i!} \left| \begin{array}{cccc} 1 & 1 & \cdots & 1 \\ (a_1+1) & (a_2+1) & \cdots & (a_n+1) \\ \vdots & \vdots & \ddots & \vdots \\ (a_1+1)^{n-1} & (a_2+1)^{n-1} & \cdots & (a_n+1)^{n-1} \\ \end{array} \right| =\prod\limits_{i=1}^n(\frac{a_i+1}{i!}\prod\limits_{1\le iM=i=1ni!ai+11(a1+1)(a1+1)n11(a2+1)(a2+1)n11(an+1)(an+1)n1=i=1n(i!ai+11i<jn(ajai))

对于后面那个 ∏ 1 ≤ i < j ≤ n n a j − a i \prod\limits_{1\le i1i<jnnajai,可以比较自然的想到用多项式乘法计算个数。

构造 f ( x ) = ∑ x − a i , g ( x ) = ∑ x a j f(x)=\sum x^{-a_i},g(x)=\sum x^{a_j} f(x)=xai,g(x)=xaj

那么我们把 f ( x ) ∗ g ( x ) f(x)*g(x) f(x)g(x)卷积之后得到的 F ( x ) = ∑ c i ∗ x i F(x)=\sum c_i*x^i F(x)=cixi,它的每个指数 i i i对应着一个 a j − a i a_j-a_i ajai,它的系数就是这个差值出现的次数,由于本题要求的都是 ∏ \prod 符号,所以我们预处理前面的阶乘和分子,后面的我们枚举全部的大于 0 0 0的幂次,做快速幂就可以实现了,还有要注意的两点,第一是实现的时候 N T T NTT NTT不能用负数做下标,所以我们要用一个偏移量 P = 1000001 P=1000001 P=1000001去处理一下负数,第二本身由于题目给了 a i < a i + 1 a_iai<ai+1所以对于相同的 x = a j − a i x=a_j-a_i x=ajai来说最多就是 n n n个,不会超过 5 ⋅ 1 0 5 5·10^5 5105个,也不用考虑其他的欧拉降幂去求解快速幂。

综上这题的时间复杂度就在 O ( n log ⁡ n ) O(n\log n) O(nlogn)时间内愉快的搞定了,挺好的一道LGV引理题和数学题。

int n;
const int P = 1000001;
int a[500005], jc[500005], inv[500005];

namespace math {
	const int MOD = 998244353;
	inline int add(int x, const int y) { return x += y, x >= MOD ? x - MOD : x; }
	inline int sub(int x, const int y) { return x -= y, x < 0 ? x += MOD : x; }
	inline int mul(const int x, const int y) { return 1ll * x * y % MOD; }
	inline int qpow(int x, int y) {
		int res = 1;
		for (; y; y >>= 1, x = mul(x, x)) if (y & 1) res = mul(res, x);
		return res;
	}
}using namespace math;

namespace NTT {
	int limit;
	int pr = 3; // 能用的ntt的素数原根
	vector<int> A, B, rev;
	void init() {
		int ed = P << 1, bit = -1;
		for (limit = 1; limit <= ed; limit <<= 1) ++bit;
		A.resize(limit); B.resize(limit); rev.resize(limit);
		for (int i = 0; i < limit; ++i)	rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << bit);
	}
	void ntt(vector<int>& P, int op) {
		for (int i = 0; i < limit; ++i) {
			if (i < rev[i])swap(P[i], P[rev[i]]);
		}
		for (int mid = 1; mid < limit; mid <<= 1) {
			int euler = qpow(pr, (MOD - 1) / (mid << 1));
			if (op < 0)	euler = qpow(euler, MOD - 2);
			for (int i = 0, pos = mid << 1; i < limit; i += pos) {
				int wk = 1;
				for (int j = 0; j < mid; ++j, wk = mul(wk, euler)) {
					int x = P[i + j], y = mul(wk, P[i + j + mid]);
					P[i + j] = add(x, y), P[i + j + mid] = add(x, MOD - y);
				}
			}
		}
		if (op > 0)	return;
		int inv = qpow(limit, MOD - 2);
		for (int i = 0; i < limit; ++i)	P[i] = mul(P[i], inv);
	}
	void work() {
		for (int i = 1; i <= n; ++i) A[a[i]] = 1;
		for (int i = 1; i <= n; ++i) B[P - a[i]] = 1;
		ntt(A, 1), ntt(B, 1);
		for (int i = 0; i < limit; ++i)	A[i] = mul(A[i], B[i]);
		ntt(A, -1);
	}
};

int solve() {
	n = read();
	jc[0] = 1;
	for (int i = 1; i <= n; ++i) {
		a[i] = read();
		jc[i] = jc[i - 1] * i % MOD;
	}
	inv[n] = qpow(jc[n], MOD - 2);
	for (int i = n - 1; ~i; --i) {
		inv[i] = inv[i + 1] * (i + 1) % MOD;
	}

	int ans = 1;
	for (int i = 1; i <= n; ++i) {
		ans = mul(mul(ans, (a[i] + 1)), inv[i]);
	}
	NTT::init();
	NTT::work();
	for (int i = P + 1; i <= 2 * P; ++i) {
		ans = mul(ans, qpow(i - P, NTT::A[i]));
	}
	print(ans);

	return 1;
}

你可能感兴趣的:(算法)