首页 > 其他分享 >Baby Ehab Plays with Permutations 题解

Baby Ehab Plays with Permutations 题解

时间:2024-09-05 09:52:58浏览次数:11  
标签:le return int 题解 sum Baby 420 Ehab mod

前言

题目链接:Codeforces洛谷

题意简述

你有一个长度为 \(n\) 的序列 \(p\) 满足 \(p_i=i\),你可以进行 \(x\) 次操作,每次操作找到两个不同的 \(i,j\) 并且交换 \(p_i,p_j\),问最终有几个可能的序列。分别求出 \(x = 1, \ldots, k\) 时的答案。

\(1 \le n \le 10^9\),\(1\le k \le 200\)。

题目分析

先考虑暴力 DP。显然原问题等价于求有多少排列经过 \(x\) 次交换后得到 \(p_i = i\)。设 \(f_i(x)\) 表示有多少长度为 \(x\) 的排列,至少经过 \(i\) 次操作可以得到原序列。边界 \(f_0(x) = 1\)。考虑转移。对于 \(f_i(x)\) 考虑 \(p_x\) 的值。若 \(p_x = x\),有 \(f_i(x) = f_i(x - 1)\);否则需要进行一次交换 \(f_i(x) = f_{i - 1}(x - 1)\)。综合得到 \(f_i(x) = f_i(x - 1) + f_{i - 1}(x - 1)\)。由于我们总是能浪费偶数次操作,所以对于一个 \(x\),答案为 \(\sum f_{x - 2t}(n)\)。

这么做时间复杂度是 \(\Theta(nk)\) 的。

显然瓶颈在于 \(n\),考虑优化掉它。发现进行 \(x\) 次操作,最多只有 \(2x\) 个关键点发生变化,不妨从这里入手。

不妨枚举有 \(i\) 个位置发生了变化,类似浪费 \(2t\) 次操作,对答案的贡献为 \(\binom{n}{i} \sum g_{x - 2t}(i)\),其中 \(g_i(x)\) 表示有多少长度为 \(x\) 的序列,每一个位置都发生了变化,至少经过 \(i\) 次操作变回原序列。

显然 \(g\) 不等价于 \(f\),因为 \(f\) 计算的时候可能存在 \(p_i = i\)。不妨找找二者关系。我们原本可以用 \(f\) 表示出答案,现在答案和 \(g\) 有关,说明 \(f\) 也能够由 \(g\) 表示出。发现如果我们类比统计答案,钦定只有某些位置发生变化,有:

\[f(x) = \sum _ {i = 0} ^ x \binom{x}{i} g(i) \]

这很二项式反演,不会的可以看看我的《学习笔记》

根据经典定理,我们得到:

\[g(x) = \sum _ {i = 0} ^ x (-1) ^ {x - i} \binom{x}{i} f(i) \]

于是问题迎刃而解。时间复杂度 \(\Theta(k^3)\)。

代码

#include <cstdio>
#include <iostream>
using namespace std;

const int mod = 1e9 + 7;

inline int add(int a, int b) {
	return a + b >= mod ? a + b - mod : a + b;
}

inline int sub(int a, int b) {
	return a - b < 0 ? a - b + mod : a - b;
}

inline int mul(int a, int b) {
	return 1ll * a * b % mod;
}

int n, k;
int f[420][420], g[420][420];

int frac[420], Inv[420], ifrac[420], tfrac[420];
void init(int n = 400) {
	frac[0] = ifrac[0] = tfrac[0] = 1;
	for (int i = 1; i <= n; ++i) {
		frac[i] = mul(frac[i - 1], i);
		Inv[i] = i == 1 ? 1 : sub(0, mul(mod / i, Inv[mod % i]));
		ifrac[i] = mul(ifrac[i - 1], Inv[i]);
		tfrac[i] = mul(tfrac[i - 1], ::n - i + 1);
	}
}

inline int C(int m) {  // C(n, m) = n * ... * (n - m + 1) / m!
	return mul(tfrac[m], ifrac[m]);
}

inline int C(int n, int m) {
	return mul(frac[n], mul(ifrac[n - m], ifrac[m]));
}

signed main() {
	scanf("%d%d", &n, &k), init(), f[0][0] = 1;
	for (int i = 1; i <= min(n, k << 1); ++i) {
		f[i][0] = 1;
		for (int j = 1; j <= k; ++j) {
			f[i][j] = add(f[i - 1][j], mul(i - 1, f[i - 1][j - 1]));
		}
	}
	for (int i = 0; i <= min(n, k << 1); ++i)
		for (int j = 0; j <= k; ++j)
			for (int x = 0; x <= i; ++x) {
				if ((i - x) & 1)
					g[i][j] = sub(g[i][j], mul(C(i, x), f[x][j]));
				else
					g[i][j] = add(g[i][j], mul(C(i, x), f[x][j]));
			}
	for (int i = 1; i <= k; ++i) {
		int res = 0;
		for (int j = min(i << 1, n); j >= 0; --j) {
			if (i >= 2) g[j][i] = add(g[j][i], g[j][i - 2]);
			res = add(res, mul(g[j][i], C(j)));
		}
		printf("%d ", res);
	}
    return 0;
}

标签:le,return,int,题解,sum,Baby,420,Ehab,mod
From: https://www.cnblogs.com/XuYueming/p/18397694

相关文章

  • Marbles 题解
    前言题目链接:Codeforces;洛谷。题意简述给定长度为\(n\)的序列\(a\),你可以交换相邻元素,请问最少交换多少次使得序列连续,即对于每种颜色,其在序列中出现的位置都是连续一段。\(m=\max\{a_i\}\leq20\),\(n\leq4\times10^5\)。题目分析对于“连续的序列”,不放看做......
  • [POI2014] RAJ-Rally 题解
    前言题目链接:Hydro&bzoj;黑暗爆炸;洛谷。题意简述DAG求删点后最长路的最小值。\(n\leq5\times10^5\),\(m\leq10^6\)。题目分析其实对于删点/边加查询最长/短路的套路是有的。比如:故乡的梦、桥。本题也类似。我们考虑,如果删除的边不在原来最长路上,那么删之后的......
  • CF1941场题解
    RudolfandtheTicket算法:枚举。题意简述:从\(a\)数组中和\(b\)数组中各选出一个数,使得它们的和不超过\(k\),求选法数量。考虑直接枚举每一种可能的搭配即可。Rudolfand121算法:贪心。题意简述:定义一次操作为,该位置上的数减去\(2\),其前一个和后一个位置(必须存在)上的数......
  • 2023 ICPC 合肥题解
    gymD.BalancedArray\(\star\)赛时做法枚举前缀维护合法的\(k\)感性上\(k\)越大需要满足的式子越少,只保留最大的\(\log\)个\(k\),可以通过std枚举\(k\),合法的\(l\)一定是一个左端点为\(2k+1\)的区间,二分右端点等式\(\forall1\lei\lel-2k,a_{i}+a_{i+2k}=2a......
  • Codeforces Round 971 (Div. 4) ABCD题详细题解(C++,Python)
    前言:    本文为CodeforcesRound971(Div.4)ABCD题的题解,包含C++,Python语言描述,觉得有帮助或者写的不错可以点个赞    比赛打了没一半突然unrated了就不是很想继续写了,早起写个题解    (之前的div3也没复盘,哎真菜)目录题A:题目大意和解题......
  • AtCoder Beginner Contest 369 题ABCD详细题解--包含解题思路和两种语言(C++,Python)
    前言:    本文为AtCoderBeginnerContest369题ABCD详细题解,包括题目大意,详细的解题思路和两种语言描述,觉的有帮助或者写的不错可以点个赞几天前的比赛拖的有点久了比赛题目连接:Tasks-AtCoderBeginnerContest369目录题A:题目大意和解题思路:代码(C++):......
  • 洛谷 B3645 数列前缀和 2 题解
    前缀知识:枚举,费马小定理,逆元,线性乘法逆元,线段树(?)。解法1:暴力如题。暴力枚举即可,30分。由于太简单,不给代码。解法2:前缀积+费马小定理+逆元由于涉及静态区间,可以想到前缀积。前缀积公式为\(q_r/q_{l-1}\),除法恰好可以用逆元来算。直接写即可。不会超时,因为时间为\(O(n\logp)\)......
  • Codeforces Round 971 (Div. 4) E 题解析
    #E题Klee'sSUPERDUPERLARGEArray!!!题目描述思路:对于这道题,首先观察到题目求的是最小可能值,而且数据的范围是1e9范围,所以首先可以考虑的方法就是O(logn)的方法,而适合求最值的方法无疑就是二分搜索,可以通过二分来不断缩小答案的区间......
  • 常见问题解决 --- 如何给一个不支持配置代理的程序抓取https流量数据
    比如我有一个C#编写票务系统,它内嵌浏览器功能,我想抓取它的流量,但是这个客户端不支持配置代理设置解决办法:1.安装配置proxifier开启全局代理服务。安装好后网上有激活码激活一下,点击profile-proxyserver,添加一个代理服务器127.0.0.1,端口8080,协议https。点击profile-proxifi......
  • 历年CSP-J初赛真题解析 | 2017年CSP-J初赛阅读程序(23-26)
    学习C++从娃娃抓起!记录下CSP-J备考学习过程中的题目,记录每一个瞬间。附上汇总贴:历年CSP-J初赛真题解析|汇总_热爱编程的通信人的博客-CSDN博客#include<iostream>usingnamespacestd;intmain(){ intt[256]; strings; inti; cin>>s; for(i=0;i<256;i......