CF1516E. Baby Ehab Plays with Permutations(组合数学)

CF1516E. Baby Ehab Plays with Permutations

Solution

因为组合水平不行所以只弄出来一个 O ( k 4 ) O(k^4) O(k4)的做法(虽然随便改改可能就 O ( k 3 log ⁡ k ) O(k^3\log k) O(k3logk)或者 O ( k 3 ) O(k^3) O(k3)了)而且因为没想清楚而自闭了很久,从而导致摸yu时间被阉割

首先大概有个显然的性质:设一个任意的操作方案形成的最终序列为 a 1 , a 2 . . . a n a_1,a_2...a_n a1,a2...an,把 i i i a i a_i ai连边,会形成若干个环,有个显然的结论是到达该状态的最少操作次数是所有环长度减一的和,而一个任意方案可以表示为最少操作方案加上若干无用操作,显然无用操作个数为偶数(由逆序对个数奇偶性可得),因此我们只需要计算最少 i i i步能到达的状态个数 A n s i Ans_i Ansi,再让 A n s i ′ = ∑ k = 0 ⌊ i 2 ⌋ A n s i − 2 k Ans'_i=\sum_{k=0}^{\lfloor\frac{i}{2}\rfloor}Ans_{i-2k} Ansi=k=02iAnsi2k即为最终的答案。

因此我们要对每一个 i i i,求出最少 i i i步能到达的状态个数。

这就相当于取若干个环,使得所有环的长度减一的和为 i i i,也就类似于做一个完全背包,取 j j j个数形成环的方案数就是环排列个数 ( j − 1 ) ! (j-1)! (j1)!,因此枚举枚举环长 i i i,枚举该种环的个数 p p p,再枚举环包含的点数 j j j和操作次数 k k k,可得:
f j , k = ∑ p f j − i p , k − ( i − 1 ) p ( i − 1 ) p ( j i p ) ∏ t = 0 p − 1 ( i p − i t − 1 i − 1 ) f_{j,k}=\sum_{p}f_{j-ip,k-(i-1)p} (i-1)^p\binom{j}{ip}\prod_{t=0}^{p-1}\binom{ip-it-1}{i-1} fj,k=pfjip,k(i1)p(i1)p(ipj)t=0p1(i1ipit1)

A n s i = ∑ j f j , i ( n j ) Ans_{i} = \sum_{j} f_{j,i}\binom{n}{j} Ansi=jfj,i(jn)

预处理一些东西即可计算。

Code

int fac[405], Inv[405], C[405][405], f[405][205], Ans[405], tmp[405][205], inv[405], Mul[405];
int quick_pow(int x, int y) {
	int ret = 1;
	for (; y ; y >>= 1) {
		if (y & 1) ret = 1ll * ret * x % mods;
		x = 1ll * x * x % mods;
	}
	return ret;
}
signed main() {
#ifndef ONLINE_JUDGE
	freopen("a.in", "r", stdin);
#endif
	int n, k;
	read(n), read(k);
	fac[0] = inv[0] = Inv[0] = 1;
	for (int i = 1; i <= k + k ; ++ i) Inv[i] = quick_pow(i, mods - 2);
	for (int i = 1; i <= k + k ; ++ i) fac[i] = 1ll * fac[i - 1] * i % mods, inv[i] = quick_pow(fac[i], mods - 2);
	for (int i = 0; i <= k + k ; ++ i) C[i][i] = C[i][0] = 1;
	for (int i = 1; i <= k + k ; ++ i)
		for (int j = 1; j < i ; ++ j) C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mods;
	f[0][0] = 1;
	for (int i = 2; i <= min(n, k + 1) ; ++ i) {
		for (int j = 0; j <= k + k ; ++ j)
			for (int t = 0; t <= k ; ++ t) tmp[j][t] = f[j][t], f[j][t] = 0;
		for (int j = 0; j <= k + k ; ++ j) Mul[j] = 1;
		for (int p = 0, mul = 1; p <= k ; ++ p) {
			for (int j = i * p ; j <= k + k ; ++ j) {
				for (int t = (i - 1) * p ; t <= k ; ++ t)
					f[j][t] = (f[j][t] + 1ll * tmp[j - i * p][t - (i - 1) * p] * Mul[i * p] % mods * mul % mods * C[j][i * p] % mods) % mods;
			}	
			for (int j = k + k; j >= 0 ; -- j)
				if (j >= i) Mul[j] = 1ll * Mul[j - i] * C[j - 1][i - 1] % mods;
				else Mul[j] = 0;
			mul = 1ll * mul * fac[i - 1] % mods;
		}
	}
	for (int i = 0, mul = 1; i <= min(k + k, n) ; ++ i) {
		for (int j = 0; j <= k ; ++ j) 
			Ans[j] = (Ans[j] + 1ll * f[i][j] * mul % mods * inv[i] % mods) % mods;
		mul = 1ll * mul * (n - i) % mods;
	}
	for (int i = 2; i <= k ; ++ i) Ans[i] = (Ans[i] + Ans[i - 2]) % mods;
	for (int i = 1; i <= k ; ++ i) print(Ans[i]), putc(' ');
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值