1. 程式人生 > 其它 >atcoder abc 226 F - Score of Permutations

atcoder abc 226 F - Score of Permutations

題目大意

有一個長度為n的排列,\(p=(p_1,p_2,p_3,p_4...,p_n)\)​,然後對於排列\(p\)​的得分\(S(p)\)遵循一下規則:

\(n\)個人,第\(i\)個人的編號為\(i\),然後第\(i\)個人有一個球,球的編號為\(i\),每次操作,第\(i\)個人會把他手裡的球給第\(p_i\)個人,然後問你最少幾次操作之後,所有人的編號和手裡球的編號相同

\(S_n\)為所有可能的\(p\)的集合,輸出\((\sum_{p∈S_n} S(p)^{k}) \,\, mod \,\,998244353\)

大概思路

\(dp_{i,j}\)表示選\(i\)個人,最少運算元為\(j\)

,操作之後,都復原的方案數.

那麼對於轉移的話,我們設下一個環的話,選\(x\)個人,從\(dp_{i,j}\)轉移過來,那麼接下來的最小運算元就是\(lcm(j,x)\),最小公倍數,轉移到的狀態就是\(dp_{i,lcm(j,x)}\)

但是 直接選的話,是有重複的,

就比如,我們 現在有\(4\)​個數,然後 \(2\)​個環,每個環都是\(2\)個人

如果直接選就是\(C_{4}^{2} * C_{2}^{2}\)

但是 如果 你 把方案都枚舉出來的話,你會發現 是有重複的,比如,會出現

\(\{1,2\}\)\(\{3,4\}\)\(\{3, 4\}\)\(\{1, 2\}\)

這樣重複的方案,在選完第一個之後,剩下的是固定的

難麼避免重複的話,我們可以 記錄一下 總的 環的數量,設為\(cnt\),那麼最後除\(cnt!\)

但是這樣的話,還是有重複的,就是說,我們在轉移的時候

假如說 現在選了\(N\)個人,接下來一個環是選\(i\)個人,那麼就是\(C_{n-N}^{i} * (i-1)!\)

那麼為什麼是\((i-1)!\) 而不是$ i!$

對於 一個環來說,在排序之後,決定它字典序大小的是最小的那個點,也就是說,

只有把最小的那個點先選上,確定之後,才能,後面的隨便選,這樣也就對應了上面

環去重,其實就是按照本質不同排序然後去重的

然後直接看程式碼

#pragma GCC optimize(2)
#include <map>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 60, mod = 998244353;
int f[N], fac[N], inv[N], n, k;
map<int, int> dp[N][N];
int qpow(int a, int b) {
	int ans = 1;

	while (b) {
		if (b & 1)
			ans = 1ll * ans * a % mod;

		a = 1ll * a * a % mod;
		b >>= 1;
	}

	return ans;
}
int lcm(int a, int b) {
	if (!a || !b)
		return a + b;

	return a / __gcd(a, b) * b;
}
int C(int n, int m) {
	return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod;
}
//  N   當前選了多少個
//  cnt 多少個環
int qry(int N, int cnt, int Lcm) {
	//到最後一個了
	if (N == n)
		return 1ll * qpow(Lcm, k) * inv[cnt] % mod;

	if (dp[N][cnt].find(Lcm) != dp[N][cnt].end())
		return dp[N][cnt][Lcm];

	int &t = dp[N][cnt][Lcm];

	//
	for (int i = 1; N + i <= n; i++)
		//				從剩下的裡面選i個
		//							先固定一個,確定先後順序
		t = (t + 1ll * C(n - N, i) * f[i] % mod * qry(N + i, cnt + 1, lcm(Lcm, i)) % mod) % mod;

	return t;
}
int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	cin >> n >> k;
	fac[0] = fac[1] = inv[0] = inv[1] = f[1] = 1;

	for (int i = 2; i <= n; i++) {
		f[i] = 1ll * f[i - 1] * (i - 1) % mod;//階乘 i-1
		fac[i] = 1ll * fac[i - 1] * i % mod;//階乘 i
		inv[i] = 1ll * inv[i - 1] * qpow(i, mod - 2) % mod;
	}

	printf("%d\n", qry(0, 0, 0));
	return 0;
}