【2020.9.12 NOIP模拟赛 T3】普及组

题目链接

求矩阵的所有行和列的乘积均为 \(k\) 的方案数。要求矩阵是 \(n \times n\) 的,由整数组成(可负)。\(T \le 2 \times 10^5,n \le 5 \times 10^6\),每组数据的 \(k\) 已知,质因数不超过二次。

首先符号的贡献显然是独立的,\(k=1\) 的贡献显然是 \(2^{(n-1)^2}\)(考虑前 \((n-1) \times (n-1)\) 随便填,用剩下一行一列来调整)。那么剩下我们要做的就是计算出正整数的贡献。

考虑到 \(k = 31 \times 37^2\)\(k = 2 \times 3^2\) 等价。具体地说,答案只与 \(k\) 的一次质因数和二次质因数的个数有关。而质因数之间又是独立的。

首先考虑一次质因数的情况。那么我们要做的就是在 \(n \times n\) 的网格中每行每列都只填一个 1 的方案数。这是个组合数学的入门题,答案是 \(n!\)

然后考虑二次质因数的情况。那么我们要做的就是在 \(n \times n\) 的网格中填若干 \(1\)\(2\),使得每行每列的和为 \(2\)。题解中有一个转化为图论问题用生成函数多项式求逆求解的方法,并不会。

考虑最后一列填的什么。如果填的是 \(2\),那么删去最后一列和那一行能够转化成子问题;如果填的是两个 \(1\),那么如果这两行的另一个 \(1\) 恰好是在同一列,那么合并并挪到最下面能够与最右边列是一个 \(2\) 的情况一一对应;如果不同,将那两行合并并放到最下面,能够与最右边列是两个 \(1\) 的情况映射,注意这里的删除合并是二对一的关系(左上右下或左下右上)。

\(f(n)\) 表示 \(n \times n\) 矩阵最右边列为一个 \(2\) 的方案数;\(g(n)\) 表示 \(n \times n\) 矩阵最右边列是两个 \(1\) 的方案数。那么转移为:

\[f(n) \gets n(f(n-1)+g(n-1)) \]

\[g(n) \gets {n \choose 2} (f(n-1)+2g(n-1)) \]

\[Ans(n) \gets f(n)+g(n) \]

关键代码

inline void init() {
	f[1] = 0, g[1] = 1;
	for (register int i = 2; i <= 5e6; ++i) {
		g[i] = 1ll * i * (g[i - 1] + f[i - 1]) % P;
		f[i] = (1ll * i * (i - 1) / 2) % P * ((f[i - 1] << 1) + g[i - 1]) % P;
	}
}
int main() {
	...
	init();
	int _; read(_); 
	while (_--) {
		int n; read(n);
		ll ans = quickpow(jie[n], can1) * quickpow((f[n] + g[n]) % P, can2) % P;
		ans = ans * quickpow(2, 1ll * (n - 1) * (n - 1) % (P - 1)) % P;
		printf("%lld\n", (ans % P + P) % P);
	}
    return 0;
}
posted @ 2020-09-14 15:36  JiaZP  阅读(182)  评论(0编辑  收藏  举报