JSOI 2019 简要题解
代码可以见 这里(LOJ 3101~3103)。
LOJ3101 「JSOI2019」精准预测
目前,火星小镇上有 \(n\) 个居民(编号 \(1, 2, \ldots, n\))。机器学习算法预测出这些居民在接下来 \(T\) 个时刻(编号 \(1, 2, \ldots , T\))的生死情况,每条预测都是如下两种形式之一:
- 难兄难弟 \(0\ t\ x\ y\):在 \(t\) 时刻,如果 \(x\) 是死亡状态,那么在 \(t + 1\) 时刻,\(y\) 是死亡状态。(注意,当 \(x\) 在 \(t\) 时刻是生存状态时,该预测也被认为是正确的);
- 死神来了 \(1\ t\ x\ y\):在 \(t\) 时刻,如果 \(x\) 是生存状态,那么在 \(t\) 时刻,\(y\) 是死亡状态。(注意,当 \(x\) 在 \(t\) 时刻是死亡状态时,该预测也被认为是正确的)。
注意本题是对某个时刻进行生死状态的预测,如果某个人在 \(t\) 时刻是生存状态,在 \(t + 1\) 时刻是死亡状态,你可以认为是在 \(t\) 到 \(t + 1\) 这段时间内发生了某个事件导致其死亡。
JYY 首先假设对火星人生死的预测全部正确,在此基础上,JYY 希望为小镇上的每个居民 \(k\) 分别计算有多少个火星人有可能和他一起活到第 \(T+1\) 时刻,换言之,JYY 希望为每个火星人 \(k\) 计算\[\sum_{1\le i\le n,i\neq k} \text{Live}(k,i) \]其中 \(\text{Live}(i, j) = 1\) 表示编号为 \(i\) 和 \(j\) 的火星人有可能同时在第 \(T + 1\) 时刻处于生还状态,否则 \(\text{Live}(i, j) = 0\)。
注意火星人是不能够复活的。一个火星人可能在时刻 \(1\) 就处于死亡状态,也有可能有预测未覆盖的死亡情况发生(火星人在任何时候都可能死亡,但任意时刻观察到火星人的状态要么活着,要么死亡)。最后,注意到 \(\text{Live}\) 是为每一对火星人分别独立计算的,因此 \(\text{Live}(x, y) = \text{Live}(y, z) = 1\) 并不意味着 \(\text{Live}(x, z) = 1\)。
\(T \le 10^6, n \le 5\times 10^4, m \le 10^5\)。
2sat
bitset
DAG
首先,可以建立 \(2n \times (T + 1)\) 个点,分别表示在每个时刻每个人的生 / 死的状态,然后可以直接根据题目条件建图了,显然无法承受。
但是我们注意到很多的点是没有用的,于是我们可以对于每个人离散化后只拿出有用的点,于是点数到了 \(2(n + m)\) 个了。
然后我们发现我们每次要找到 \(x\) 在 \(T+1\) 的时候活着,就会推出一些人必须死亡,于是我们可以用一个 bitset 存一下如果一个点代表的条件最后成立,那么最后哪些人会死亡。
同时,我们注意到这个题目一定有解(开局全部死亡),那么一定不会存在 scc
,这个图一定是一个 DAG
,于是我们可以记忆化搜素计算这个 bitset
。
于是时空复杂度就是 \(\mathcal O(\frac{n^2 + nm}{\omega})\)。
但是空间还是太大,于是我们可以一次只计算对于 \(B \approx 10^4\) 个点的答案,然后就可以节省空间了。
LOJ3102 「JSOI2019」神经网络
火星人在出生后,神经网络可以看作是一个由若干无向树 \(\{T_1(V_1, E_1), T_2(V_2, E_2),\ldots T_m(V_m, E_m)\}\) 构成的森林。随着火星人年龄的增长,神经连接的数量也不断增长。初始时,神经网络中生长的连接 \(E^\ast = \emptyset\)。神经网络根据如下规则生长:
- 如果节点 \(u \in V_i, v \in V_j\) 分别属于不同的无向树 \(T_i\) 和 \(T_j\ (i \neq j)\),则 \(E^\ast\) 中应当包含边 \((u, v)\)。
最终,在不再有神经网络连接可能生长后,神经网络之间的节点连接可以看成是一个无向图 \(G(V,E)\),其中
\[V=V_1\cup V_2\cup \ldots \cup V_m,E=E_1\cup E_2\cup \ldots \cup E_m\cup E^\ast \]火星人的决策是通过在 \(G(V, E)\) 中建立环路完成的。针对不同的外界输入,火星人会建立不同的神经连接环路,从而做出不同的响应。为了了解火星人行为模式的复杂性,JYY 决定计算 \(G\) 中哈密顿回路的数量。
\(G(V, E)\) 的哈密顿回路是一条简单回路,从第一棵树的第一个节点出发,恰好经过 \(V\) 中的其他节点一次且仅一次,并且回到第一棵树的第一个节点。
\(\sum_{i=1}^m k_i \le 5\times 10^3, m\le 300\)。
动态规划
背包
组合数学
容斥
生成函数
原题相当于将每棵树分成若干条互不相交的链,然后将所有链任意排列后组成一个相邻元素互不相同的环排列。
考虑 DP,设 \(f(x, i, 0 / 1 / 2)\) 表示将 \(x\) 子树分成 \(i\) 条链,其中 \(x\) 所在的链的状态是 只有 \(x\) 一个点 / 是一条可以加点的链(非单点) / 不能加点的链的方案数。
转移的时候讨论一下即可,注意当一条可以加点的链变成不能加点的链后,链有两种方向,于是方案要 \(\times 2\)。
之后我们设 \(f_i\) 表示将一棵树分成 \(i\) 条链的方案数,先不考虑环和第一个点的限制,那么我们枚举一个数组 \(\{a_i\}\) 表示将每棵树分成 \(a_i\) 条链,然后我们要求将 \(a_i\) 条链排列在一起,且相邻两个元素所属的树不同。
考虑容斥,枚举 \(\{b_i\}\) 表示我们将每个树的链分成若干个块,那么有:
\(\frac{(\sum b_i)!}{\prod b_i!}\) 表示排列所有的块,\((-1)^{a_i - b_i}\) 是容斥系数,\(\binom{a_i - 1}{b_i - 1}\) 表示将所有链划分成 \(b_i\) 个块的方案数,再乘上 \(a_i!\) 表示将所有链都是不同的,因此任意排列。
因为我们可以将 \(\frac{1}{\prod b_i!}\) 乘到后面去,于是我们可以把每棵树的方案表示成 \(\rm EGF\) 的形式:
将所有 \(\rm EGF\) 乘起来即可。
但是有 \(1\) 的限制,我们强制 \(1\) 在所在的链是第一条链且 \(1\) 是第一个点,那么有:
因为还有环排列,于是还要减去:
暴力卷积就行了。
LOJ3103 「JSOI2019」节日庆典
给出字符串 \(S\),对于其每个前缀求最小循环表示。
\(1 \le |S| \le 3\times 10^6\)。
字符串
拓展 KMP
最小循环表示法
我们对于每个前缀 \(S\) 依次求答案,同时维护一个 \(\rm candidates(S)\) 表示满足“存在 \(T\) 使得 \(ST\) 的最小后缀是 \(S_0T\)”后缀 \(S_0\) 构成的集合。讲人话,就是还有希望成为最小循环表示的后缀。
然后我们发现,\(\rm candidates(S)\) 中相邻的两个后缀长度要翻倍,于是 \(|\rm candidates(S)|\) 是 \(\log_2 |S|\) 级别的了。
证明(\(\tt from \;zsy\),略有修改):
考虑相邻两个最小后缀 \(x, y\),如果 \(|y| < |x| < 2|y|\),则可以令 \(x = AAB, y = AB\),其中 \(A,B\) 都为字符串,并且 \(B\) 是 \(A\) 的一个严格前缀。对于 \(AAB, AB, B\) 这三个后缀来说,无论如何 \(AB\) 都是不是最优的,于是可以把 \(AB\) 剔除,于是证明了相邻后缀长度翻倍。
只要一边跑,一边维护每个 \(\rm candidates(S)\) 就行了。
然后我们还要从 \(\rm candidates(S)\) 中取出一个最优的作为答案,因此关键是比较两个后缀 \(S[x \dots i], S[y \dots i](x > y)\),我们发现,我们目前就已经保证了 \(S[y\dots i] = S[x\dots x + i - y]\),于是我们接下来的问题都是求 \(\rm lcp(1, *)\),于是可以直接 \(\tt exKMP\) 解决。