题解 P6103 【[EER2]直接自然溢出啥事没有】

xht

2020-02-18 14:16:57

Solution

考虑递推,设 $f_i$ 表示长度为 $i$ 的「程序片段」,有 $f_0 = 1$。 注意到,「程序片段」只能是「程序片段」$+$「语句」,若设 $c_i$ 表示长度为 $i$ 的「语句」个数,则有 $f_{i} = \sum_{j=0}^{i-1} f_jc_{i-j}$,则可以 $\mathcal O(n^2)$ 递推。 考虑什么东西能当「语句」。 首先,`;` 为一个「语句」,因此有 $f_i \leftarrow f_{i-1}$。 除此之外,「语句」都必须由另外一个「程序片段」生成。 我们枚举这个「程序片段」的长度 $j$,设其为 `A`,则 `A` 的个数为 $f_j$。 它会生成 `{A}`「语句块」和 `{A}`「语句」,此时出现了「语句」,则有 $f_i \leftarrow f_{i-j-2} \times f_{j}$。 如果此时我们还想生成「语句」,那么只能走这样一条路:「语句」$\to$「函数」$\to$「值」$\to$「语句」 。 然后就无法再生成更多的「语句」了。 「语句」$\to$「函数」的过程实际上就是长度 $+2$ 或 $+4$。 「值」$\to$「语句」的过程实际上就是长度 $+1$。 我们只需要考虑「函数」$\to$「值」的过程,这也是最复杂的一个过程。 对于一个长度为 $l$ 的「函数」`A`,它会变成: - 一个长度为 $l$ 的值 `A`; - 两个长度为 $l+2$ 的值 `(A),A()`; - 三个长度为 $l+4$ 的值 `((A)),(A()),(A)()`; - 四个长度为 $l+6$ 的值 `(((A))),((A())),((A)()),((A))()`; - $\cdots$ - $n$ 个长度为 $l+(n-1)\times 2$ 的值。 考虑到「语句」$\to$「函数」的过程长度 $+2$ 或 $+4$,「值」$\to$「语句」的过程长度 $+1$。 将这些信息整合起来可以得到 $f_i \leftarrow f_j \sum_{k=1}^{i-j-4} [k \bmod 2 = 1]k \cdot f_{i-j-4}$。 于是我们可以写出下面这个 $\mathcal O(n^3)$ 的程序: ```cpp const int N = 1e4 + 7; int n; ul f[N]; int main() { rd(n), f[0] = 1; for (int i = 1; i <= n; i++) { f[i] = f[i-1]; for (int j = 0; i - j - 2 >= 0; j++) { ul o = f[i-j-2]; for (int k = 1; i - j - k - 4 >= 0; k += 2) o += f[i-j-k-4] * k; f[i] += o * f[j]; } } print(f[n]); return 0; } ``` 然而,数据范围要求我们在 $\mathcal O(n^2)$ 的时间复杂度内解决。 考虑优化,注意到 $\sum_{k=1}^{i-j-4} [k \bmod 2 = 1]k \cdot f_{i-j-4}$ 这玩意儿可以前缀和优化 $\mathcal O(1)$ 得到,于是时间复杂度降为 $\mathcal O(n^2)$。 至于对 $2^{64}$ 取模,题目名已经告诉我们怎么做了。 ```cpp const int N = 1e4 + 7; int n; ul f[N], g[N]; int main() { rd(n), f[0] = g[1] = 1; for (int i = 1; i <= n; i++) { f[i] = f[i-1]; for (int j = 0; i - j - 2 >= 0; j++) f[i] += f[i-j-2] * f[j]; for (int j = 0; i - j - 4 >= 0; j++) f[i] += g[i-j-4] * f[j]; for (int j = 1; i + 1 - j >= 0; j += 2) g[i+1] += f[i+1-j] * j; } print(f[n]); return 0; } ```