USACO23DEC P

发布时间 2024-01-04 16:21:37作者: came11ia

A. Cowntact Tracing

设病牛集合为 \(S\)。首先用多源 BFS 可以求出哪些位置可能初始有病,即满足 \(S_k(u) = \{v | d(v,u) \le k\} \in S\) 的所有 \(u\)。有解当且仅当这些位置全染上之后合法,这个可以再跑一次多源 BFS 看看是不是每个位置都能被染上。

解决了合法性之后就可以大力贪心了。我们随便定个根,然后在满足子树内部能全染上的情况下我们尽量往根放,这样可能可以多染到别的子树内的东西。所以策略就是,对于 \((u,fa_u)\),除非在 \(fa_u\) 放没救了我们才在 \(u\) 这里放一个,把 \(u\) 子树全盖完。这里在 \(u\) 放指的就是说,找一个离 \(u\) 最近的可以放的放了。然后可以证明这样每次最多只需要放一个点。感性理解一下,你能盖到最深的子树,那别的子树也没道理盖不到。 正确性可以看官方题解。

每个子树的最优方案可以用一个状态 \((-1\sim 1,d)\) 表示,\(-1\) 表示子树内最深的没被盖的点深度为 \(d\)\(0\) 表示没东西,\(1\) 表示全被盖完了并且往上延伸了 \(d\),状态容易 \(\mathcal{O}(1)\) 合并,因此每次复杂度 \(\mathcal{O}(n)\)

B. A Graph Problem

按边的编号为权值建 kruskal 重构树,一条边连接两个连通块对一个点的答案会发生:先走完其连通块内的所有边,再走这条边,再以这条边的端点为起点走另一个连通块。也就是这个点更新后的答案为原先答案、这条边、对面端点答案三者顺次拼接。可以直接拍成 DFS 用线段树维护,也可以发现由于操作顺序是自底向上,给整个子树更新的时候可以直接在并查集的边上打加乘标记,查询的时候一路合并上去就行了。时间复杂度 \(\mathcal{O}(n \log n)\)

C. Train Scheduling

主要的问题就是我们不能在状态里记录时间。那咋办,首先你状态里肯定有当前拿到了哪个位置,那我们不妨钦定一下时间就是对应的 \(t_i\),即设 \(f_{i,j}\) 表示 \(A,B\) 分别走了 \(i,j\) 的前缀,最后一次走 \(A\) 并且出发时间恰好就是 \(ta_i\)\(g_{i,j}\) 表示最后一次走 \(B\) 并且出发时间恰好就是 \(tb_j\)。转移考虑是否换方向,不换就是 \(f_{i,j} \gets f_{i+1,j}\),换的话就是 \(f_{i,j} \gets g_{i,j+1}\),但是需要满足 \(tb_{j+1} \geq ta_i + T\),否则不满足定义。

\(tb_{j+1} < ta_i + T\) 的转移就被漏掉了,咋办。注意到这个时候最优策略一定是形如两边交替走 \(T\) 的时间,后面接上我们定义的状态。这个最多走不超过 \(\mathcal{O}(n)\) 次,直接暴力算,取最优的次数即可。这样是 \(\mathcal{O}(n^3)\) 的。再注意到,对于每个 \(i\)\(j\) 的第一步一定会走到最后一个 \(tb_c < ta_i + T\) 的位置 \(c\),后面的部分全都是一样的。所以本质不同的走法只有 \(\mathcal{O}(n)\) 种,可以预处理出来。第一步的贡献容易通过前缀和 \(\mathcal{O}(1)\) 计算,总时间复杂度 \(\mathcal{O}(n^2)\)