题解 ABC226F Score of Permutations

定义一个排列的分数为开始时第 \(i\) 个人拿着 \(i\) 号球,每次 \(i\) 把球给 \(p_i\),需要第二次达到开始状态的次数。求长度为 \(n\) 的所有排列的分数的 \(k\) 次方的和。

这场 ABC E 一些小问题调得太久了,最后的时间里没有能做出这题了。虽然多个二十分钟可能还是做不出

首先看到这个次幂的就知道不会有什么神奇的性质了,得老老实实把每个答案都求出来了。

容易得到对于一个排列答案肯定是所有环大小的最小公倍数,那么设 \(f(i, j)\) 表示 \(i\) 个构成最小公倍数是 \(j\) 的方案数。
那么每次加入新的环时就乘上选出 \(k\) 个的方案以及它们构成环的方案数就可以了。\(n\) 个点构成环的方案数就是全排列一下然后除以 \(n\)(因为每个重复记了 \(n\) 次),即 \((n-1)!\)
但若每次用一个组合数加入那么显然会出现重复,因为一个环先加后加都没有区别,那么得找到一种方法使剩下一些确定的点时若要加一个大小确定的环方案是唯一的。其实很简单,只需要规定必须选中剩下的编号最小的就可以了。

\[f(i, \text{lcm}(j, k)) += f(i, j){n-i-1 \choose x-1}(k-1)! \]

这个实现的时候可以用 map 来遍历有意义的状态。

#include <bits/stdc++.h>
#define int long long
constexpr int N = 55, P = 998244353;
int n, k, ans, fac[N], inv[N];
std::map<int, int> f[N];
int Pow(int a, int b) {
	int an = 1;
	for ( ; b; b >>= 1, a = a * a % P)
		if (b & 1) an = an * a % P;
	return an;
}
void add(int &x, int y) { x = (x + y) % P; }
int gcd(int a, int b) { return !b ? a : gcd(b, a % b); }
int lcm(int a, int b) { return a / gcd(a, b) * b; }
void init(int n) {
	fac[0] = 1;
	for (int i = 1; i <= n; i++) fac[i] = fac[i-1] * i % P;
	inv[n] = Pow(fac[n], P-2);
	for (int i = n; i >= 1; i--) inv[i-1] = inv[i] * i % P;
}
int C(int n, int m) {
	if (n < m) return 0;
	return fac[n] * inv[m] % P * inv[n-m] % P;
}
signed main() {
	std::cin >> n >> k;
	init(52);
	f[0][1] = 1;
	for (int i = 0; i < n; i++)
		for (auto c : f[i])
			for (int j = 1; i + j <= n; j++) 	
				add(f[i+j][lcm(j, c.first)],
					c.second * C(n-i-1, j-1) % P * fac[j-1] % P);
	for (auto c : f[n]) add(ans, c.second * Pow(c.first, k));
	std::cout << ans;
}
posted @ 2021-11-09 19:29  Acfboy  阅读(147)  评论(0编辑  收藏  举报