Knjige
将左书架上的书第 (k) 小放到其合适的位置上最多需要 (4n) 步。具体步骤:
- 将左书架上第 (k) 小移到右书架顶。
- 将右书架顶的书移回左书架顶。
- 其余书移回左书架。
最多需要 (4n^2) 次操作,可以通过。
Vlak
将所有字符串插入一棵 ( ext{trie}) 内,对于不同颜色分别打上 ( ext{tag})。以 (1) 为起始深度求出 ( ext{trie}) 内每个点的深度。我们发现 ( ext{trie}) 树上一条从根到任意节点的路径就是一种博弈方式,且深度为奇数时是 ( ext{Nina}) 行动。双方行动时都会从当前节点出发,可以走到它的任意一个儿子节点,这是不是有点像有向图博弈?所以就可以用有向图博弈的方式解决它:当且仅当一个点存在至少出边指向先手必败节点,此节点为先手必胜节点。
当一个节点无法到达它的儿子节点,这个节点也为先手必败节点。利用这个边界条件,使用 ( ext{dfs}) 自底向上遍历整棵 ( ext{trie}) 即可。
Sateliti
考虑如何处理题目中所说的“环形四向平移”。我们知道对于序列问题,加倍复制,断环成链是非常常见的一种解法。这题也可以这么做:将 (n imes m) 的矩形复制 (3) 份,即扩大一倍,得到一个 (2n imes 2m) 的矩形。原矩形“环形四向平移”所能得到的所有矩形都是这个 (2n imes 2m) 的一个子矩形。暴力枚举子矩形左上端点求解是 (O(n^2m^2)) 的,不能承受。
考虑这个做法时间复杂度的瓶颈在枚举左上端点后,对任意两个子矩形字典序大小的比较。可能会想到一种不取模的 ( ext{hash}),但值域过大无法承受。其实想到 ( ext{hash}) 已经很接近答案了。
借助二分,我们可以使用取模的 ( ext{hash}) 来解决这个问题。( ext{hash}) 的功能主要是比较两个字符串是否相等。根据字典序的定义,我们可以二分一个两字符串第一次不相等的点。( ext{hash}) 值可以使用二维 ( ext{hash}) 和二维前缀和预处理出来。这样我们就使用简单的二分 (+) ( ext{hash}) 完成了对两个子矩阵字典序大小的比较。比较部分是 (O(log n+log m)) 的,而预处理部分是 (O(nm)) 的。
最后,这里解释一下什么是二维 ( ext{hash}),其实就是由一维 ( ext{hash}) 的进制位 (p^i) 变为 (p^iq^j),其中 (i) 与行有关,(j) 与列有关。值得注意的是,(gcd(p,q)=1)。
Selotejp
我们考虑一些胶带,如果它们是同向的,且中间没有断掉,事实上可以使用同一条胶带。又发现 (m) 很小,所以可以考虑 ( ext{bitmask}) 意义下的 ( ext{DP})。显然有一个很 ( ext{Naive}) 的 ( ext{DP}),设 (f[n,S]) 为第 (n) 行的状态为 (S)(其中 (S) 在二进制意义下第 (i) 位为 (0) 表示 ((n,i)) 位置被横胶带覆盖或 ((n,i)) 位置非法,否则说明被纵胶带覆盖)。那么我们有一个 (O(2^mnm+4^mn)) 的 ( ext{DP}),可以通过 ( ext{Subtask 1,2})。
我们考虑这个 ( ext{DP}) 是否还可以优化。似乎找不到优化点了。对当前行状态和上一行状态的枚举是不可避免的,而这正是时间复杂度瓶颈。我们考虑变化定义,设 (f[n,m,S]) 表示当前方格为在第 (n) 行第 (m) 列,最后 (m) 个方格的状态为 (S)。我们惊奇的发现,时间复杂度居然可以达到 (O(2^mnm))。从状态 (f[n,m-1,S]) 或 (f[n,m-1,Soplus2^m]) 可以得到 (f[n,m,S]),随便推一推转移方程就好了(
Specijacija
( ext{Subtask 1}) 可以直接构造树。( ext{Subtask 2}) 可以直接向上跳。这两个 ( ext{Subtask}) 都非常简单,似乎并没有什么指导意义(
我们注意到这题有一个重要性质:第 (i) 层只有一个节点具有两个儿子,也就是说,树的形态中会包含很多长链。如下图,被红色圆圈圈出的是一些长链:
如果我们将这些长链缩成点,并保留连通性,得到一棵新树,会怎么样呢?我们惊奇的发现新树的点数为 (O(n))。证明如下,假设每个叶子节点都是一条长链,那么初始有 (n+1) 条长链。每个具有两个儿子的点都会使得长链个数 (+1)。故长链个数不超过 (2n+1),即长链个数是 (O(n)) 级别的。
假设我们已经建出了这棵新树,那么我们就可以直接在新树上查询 ( ext{lca}) ,从而得到原树上 (x,y) 的 ( ext{lca}) 所在的长链的编号。现在需要解决两个问题:如何建出新树以及如何查询任意一个点所在的长链编号。
显然有一个性质,新树上的边只会是那些 (a_i) 与其儿子相连的边。可以想到,自底向下考虑每个 (a_i)。这样的话,从第 (i) 层到第 (i-1) 层,某个 (a_i) 具有两个儿子对第 (i-1) 层节点的影响,可以看作第 (i) 层的节点删去右节点得到。而如果从第 (i-1) 层到第 (i) 层,则 (a_{i-1}+i) 之后的点都向后移了一位。
像这种动态删除,并查询实际第 (k) 个位置,可以使用线段树实现,对每层开一个线段树,叶子上保存当前节点所在的长链编号。并且发现每层除了与 (a_i) 相关的点,其他点信息都没有改变,所以可以使用可持久化线段树不至于 ( ext{MLE})。建树的时候随便记录一下原树上当前长链深度最大的节点,就可以在查询到 ( ext{lca}) 所在长链编号后直接得到 ( ext{lca}) 了。