ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

atcoder abc 226 F - Score of Permutations

2021-11-08 18:34:24  阅读:196  来源: 互联网

标签:atcoder cnt abc return int inv Permutations dp mod


题目大意

有一个长度为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;
}

标签:atcoder,cnt,abc,return,int,inv,Permutations,dp,mod
来源: https://www.cnblogs.com/QingyuYYYYY/p/15525487.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有