zoukankan      html  css  js  c++  java
  • atcoder abc 226 F

    题目大意

    有一个长度为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
    ", qry(0, 0, 0));
    	return 0;
    }
    
  • 相关阅读:
    mac 使用tree命令
    为什么redis支持lua脚本功能
    redis协议
    Linux的SOCKET编程详解
    大型网站架构之分布式消息队列
    自定义String
    逆转单链表
    单例模式 C++
    构造函数不能为虚函数
    Windows消息机制
  • 原文地址:https://www.cnblogs.com/QingyuYYYYY/p/15525487.html
Copyright © 2011-2022 走看看