题解:CF2207E2 N-MEX (Counting Version)

· · 题解

全场最短且最简单的代码。

首先考虑数组 b 的限制。假如当前是第 (n-i+1)\operatorname{mex} ,不难发现, b[i...n] 必须满足:

假设 c_i 表示前 i 个元素构成的集合里,落在

由于 $c_i$ 表示 $[0, a_i-1]$ 中出现过的元素,因此 $c_i$ 必定满足 $c_i\in[0,i]$ 。除此之外,要想存在这样的数组,还有需要一个条件,就是序列本身必须单调不升,即 $\forall i\in[2,n],a_i\leq a_{i-1}$ 。 证明也比较简单。我们都知道, $\operatorname{mex}$ 满足一个定理:设两个集合 $A$ , $B$ 满足 $A \subseteq B$,则对于 $\forall t \in \mathbb{N}_+, t\operatorname{mex}(B) \leq (t+1)\operatorname{mex}(A)$ 。随着 $i$ 的增大,集合元素个数肯定单调不降,又因为 $n-i+1$ 也就是式子里的 $t$ 单调不升,因此代入不等式,自然对应的 $\operatorname{mex}$ ,也就是 $a_i$ 单调不升成立。 接下来,就可以分类讨论 $a_i$ 和 $a_{i-1}$ 的情况,显然有两种: 1. 如果 $a_i = a_{i-1}$ ,那么此时就需要填一个小于 $a_i$ 且从未出现过的数,这些数一共有 $a_i-n+i=c_i$ 个。 2. 如果 $a_i < a_{i-1}$ ,那么需要插入一个要么被用过要么比 $a_i$ 大的数,而方法数就是 $i$ 。 根据乘法原理,直接乘起来即可。 最后说一下怎么优化时空复杂度(虽然题目不卡)。由于每一次遍历关注的对象都只有 $a_i$ 和 $a_{i-1}$ ,既不受前面的影响也不受后面的影响,因此完全没有必要使用数组,用两个滚动变量表示 $a_i$ 和 $a_{i-1}$ ,只用一次遍历,边输入边处理即可。 时间复杂度 $O(Tn)$ ,空间复杂度 $O(1)$ ,常数极小。 ```cpp #include<iostream> #define LoveCyrene ios :: sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr) using namespace std; signed main(){ LoveCyrene; long long T, n, x, y, i, s; cin >> T; while(T--){ cin >> n; for (i = 1, y = n, s = 1; i <= n; i++, y = x){ cin >> x; long long k = x - n + i; if(k > i || k < 0 || (x > y)) s = 0; s = s * (x == y ? k : i) % 1000000007ll; } cout << s << '\n'; } }