NOI2023
NOI2023 题解
应该是全网首发?
D1T1 方格染色
shaber 题。
首先假设只有横竖线,总答案等于横线的并 + 竖线的并 - 横竖的交。前面二者排序后容易计算,后面考虑按照 \(x\) 递增顺序依次加入竖线,同时扫描线维护横线,在加入新的竖线的时候减去此时在区间的横线个数,可以用线段树 / 树状数组维护。
有斜线时,还是对斜线求并,再减去公共部分,可以暴力枚举所有与其相交的横竖线,对本质不同的交点求和。
时间复杂度 \(\mathcal O(n \log n)\)。
D1T2 桂花树
抽象题意:对于一个 \(1 \leq i \leq n\) 的 \(i\) 而言, \(i\) 分别在 \(T\) 上的一级祖先,二级祖先等分别与 \(T'\) 上只保留 \(\leq n\) 的点构成的虚树上的父亲完全相同。也就是 \(T\) 和 \(T'\) 的虚树相同。
一个 \(>n\) 的点要么插入边中,要么挂在一个点上作为儿子。于是当 \(m = 0\) 时答案为 1,\(m = 1\) 时答案为 \(2n - 1\)(\(n\) 种方案挂在点下,\(n - 1\) 种方案插入边中),\(m = 2\) 时为 \((2n - 1)(2n+1) +[k>0](n - 1)\),因为 \(k>0\) 时 \(n+1\) 和 \(n+2\) 可能以 “ \(n+2\) 插入边,\(n +1\) 作为 \(n+2\) 的儿子” 的形态存在,而此时的插入方式可以看做 “先预留一个编号在 \([i+1, i+k]\)” 的点插入边中,\(i\) 再作为其儿子“
进一步发现可以从小到大加入点,设 \(dp_{i, S}\) 表示考虑到第 \(n+i\) 个点,目前预留点的状态为 \(S\) 的方案(\(S\) 的第 \(p\) 位表示有一个 \(n+i - p\) 的限制),此时总点数为 \(t = n+i +|S|\),转移:
- 挂在点或边上:$dp_{i+1, S<<1} \leftarrow dp_{i, S} \times (2t - 1) $。
- 预留一个点:\(dp_{i+1, S <<1 | 1} \leftarrow dp_{i, S} \times (t - 1)\)。
- 解决一个点的预留:找到一位 \(p\) 为 1 设为 0 再左移转移即可。
发现答案和树的形态无关,时间复杂度 \(\mathcal O(mk2^k)\)。
D1T3 深搜
以 \(s\) 为根 \(T \cup E\) 的生成树为 \(T\) 的充要条件为 \(E\) 中所有边在 \(T\) 中都是返祖边。即 dfs 树的定义。
首先容斥:钦定非空关键点集 \(S\),求得在所有 \(u \in S\) 为根下都是返祖边的额外边个数 \(cnt\),贡献即 \((-1)^{|S| - 1}2^{cnt}\)。暴力 \(kn2^k\) 计算 36pts。
再考虑链,判掉 \(1\) 或者 \(n\) 为关键点的情况。设 \(dp_x\) 表示 \(x\) 为最后一个被选点的方案,转移:\(dp_x = -\sum\limits_{y < x}dp_y2^{cnt_{y, x}}\),其中 \(cnt_{y, x}\) 为包含于 \([y, x]\) 的边的个数,从小到大转移,枚举 \(x\) 时若存在 \((y, x)\) 边,则把 \([1, y]\) 的 dp 值全部 \(\times 2\),最后查询全局和,可以用线段树维护,\(\mathcal O(n\log n)\)。
拓展到树上,考虑选定的关键点在树上的分布,对于一条额外边 \((u, v)\),则要求所有关键点全部在 \(u\) 的子树或者 \(v\) 的子树,不存在夹在内部的情况,于是可以想到建出关键点的虚树,所有边必定是夹在两个虚树上的点,或者在这些点的子树内。但有一种特殊情况:虚树的根是一个虚拟节点,且恰好有两个儿子,此时在两个儿子到根的路径上的边可以选择。没有这种情况时,恰好对应了特殊性质 A。
先预处理 \(f_x, g_x\) 分别表示 \(x\) 子树内 / 外的边的个数,答案统计在虚树的根上,贡献为 \(dp_x \times g_x\)。
当 \(x\) 子树内有 \(\geq 2\) 个不同子树的点选择时,\(x\) 会被自动选择,于是需要背包。如果 \(y\) 作为 \(x\) 的子树内点被选,还要乘上 \(x \to y\) 上的边,边上的点在异于儿子子树的子树内边的个数。同样可以用以 dfs 序为下标的线段树维护:第一类边直接在 \([L_y, R_y]\) 区间乘即可,第二类边在处理完 \(x\) 后枚举儿子 \(y\),\([L_y, R_y]\) 乘上其他子树的边的个数。如果 \(y\) 子树选点,贡献为子树和,否则为子树边条数。然后根据 \(x\) 是否为关键点讨论即可。
时间复杂度 \(\mathcal O(n \log n)\)。
最后处理存在横叉边,根恰好有两个儿子的情况,此时 \(g_x\) 会而外加上在 1 为根意义下为横叉边,但 \(x\) 意义下为返祖边条数,其他不变,同样预处理,为了区分选了 2 个或 \(>2\) 个需要多背包一下。
枚举虚根 \(x\),此时线段树内已经求得 \(x\) 到某个儿子的方案 \(dp_u\)。枚举两个异侧子树点 \(u, v\),贡献为 \(dp_u \times dp_v\) 乘上其它子树不选的方案。先求出 \(prd = \prod\limits_{y} 2^{f_y}\),每个子树将 \([L_y, R_y]\) 除以 \(2^{f_y}\),这样只和两个子树有关。将横叉边挂在其 lca 处,对于一个横叉边 \((u, v)\),相当于把 \(([L_u, R_u], [L_v, R_v])\) 的点权值 \(\times 2\),然后查询全局矩形和。可以扫描线,然后只用考虑一段极小区间 \([l, r]\),再开另一棵线段树维护第二维的区间乘,累加第一棵线段树上 \([l, r]\) 之和乘上第二棵线段树全局和。然后再减去两个点在同一个子树的情况。
时间复杂度 \(\mathcal O(n \log n)\)。
D2T1 贸易
shaber 题。
先处理 \((x, y)\) 互为祖先关系的,如果 \(x\) 是 \(y\) 祖先就将 \(x\) 子树中所有点和祖先加入跑 dijkstra,注意不要加入祖先的额外边,因为 \(x\) 到祖先的最短路可以直接求。
如果不互为祖先,枚举 \(\mathrm {lca} = x\),必然是 \(u\) 不断跳到 \(x\) 然后只和 \(x\) 有关,随便算算就好。
时间复杂度 \(\mathcal O(2^nn^2)\)。
D2T2 字符串
题目要求非常奇怪,不妨先拓宽条件,变成 \(s[i:i+2l - 1] <R(s[i:i+2l - 1])\),容易发现二者等价。
将所有前缀和后缀一起跑 SA 得到每个前后缀 rk,又可以转化为 \(rk1_i <rk2_{i+2l - 1}\),这部分可以直接二维数点,但可能存在 \(s[i:i+2l - 1]\) 为回文串,第一个不回文位置 \(u, v\) 满足 \(s_u > s_v\),此时同样导出 \(rk1_i < rk2_{i+2l - 1}\),需要减去。
用 manacher 求得每个偶回文串 \(p\),的回文半径,设左右端点分别为 \(L, R(s_{L - 1}>s_{R+1})\),则会对一个询问 \((i, r)\) 有贡献当且仅当 \(L \leq i \leq p \and i+2r - 1 \geq p\),同样二维数点。
时间复杂度 \(\mathcal O(n \log n)\)。我才不会告诉你考场上写 SA 调了半个小时。
D2T3 合并书本
将合并过程看做二叉树,首先有显然 \(\mathcal O(3^nn^2)\) 做法,当 \(w_i = 1\) 可以用一个 \(\mathcal O(n^4)\) dp 代替。
然后可以考虑乱搞,包括但不限于模拟退火退树的 prufer 序列,以及将原序列随机打乱做 \(\mathcal O(n^4)\) 的 dp,但是没有实质性进展。
不懂就问:
理性分析,推推性质,猜猜结论,得到了一个复杂度是拆分数乘以 \(\mathrm {poly}(n)\) 的做法。
是 tm 什么人类。
考虑从根节点开始不断加叶子,看做每个点有一个覆盖次数 \(c_i\),每次加两个叶子等价于加入 \(c_i+1\)。 当树的形态固定时磨损时一定,于是需要记录的状态有最后一层的 \(\{c_i\}\) 的可重集 \(S\),当前磨损值 \(w\)。
每次选择一个 \(T \subseteq S\),加入 \(c_i+1\),同时 $ w \leftarrow 2(w+|T|)$ (建议多画几个图理解一下)。
然后考虑如下事实:
- 根据排序不等式,最后一定是大的权值和小的 \(c_i\) 配对,小的权值和大的 \(c_i\) 配对。
- 一定可以以 \(|T|\) 单调不降加入,反之可以将前面几次放到后面,且最后 \(|T| = n - 1\)。
- 每次令 \(c_i +1\) 的 \(c_i\) 一定是从小到大排序后的前缀(大了一定不优秀)。
根据如上策略,可以搜 \(n\) 的拆分作为每次 \(|T|\),由于拆分 \(p(n) \sim \dfrac{1}{4n\sqrt 3}\exp(\pi \sqrt{\dfrac{2n}3})\),直接搜 65pts。
注意到相同的 \(S\) 只用保留最小的 \(c\),相同的 \(lst\) 只用保留最小的 \(lst\),设 \(dp_{S,lst}\) 表示目前扩展到 \(S\),上一个大小为 \(lst\) 的最小的 \(c\) ,用 map 记录 dp 状态然后用上面的转移就行了。时间复杂度 \(\mathcal O(wys)\)。