NOI D1T1 合集
为了防止菜鸡 Scintilla 在 NOI2021 网络同步赛中保龄,他找了一些 NOI D1T1 来做。
NOI2020 D1T1 美食家
Description
给定一张 \(n\) 个点、\(m\) 条边的有向图,点 \(u\) 有点权 \(c_u\)、边 \(i\) 有边权 \(w_i\),有重边无自环。你需要在第 \(0\) 秒从 \(1\) 号点开始走,走一条边的秒数为其权值,经过一个点 \(u\) 可以获得 \(c_u\) 的收益。特别地,有 \(k\) 个特殊情况,每组特殊情况形如 \(t, x, y\),表示在第 \(t\) 秒走到点 \(x\) 可以额外获得 \(y\) 的收益。你需要在恰好第 \(t\) 秒回到 \(1\) 号点,求最大收益。
\(n \le 50, n \le m \le 501, k \le 200, w_i \le 5, c_i \le 52501\)。
\(1 \le u_i, v_i, x_i \le n, t_i \le T \le 10^9, y_i \le 10^9\)。
Solution
看到 \(n\) 很小、\(T\) 很大,第一时间想到矩乘。
一个显然的想法是设 \(f_{i, j}\) 表示第 \(i\) 秒到达 \(u\) 号点的最大收益、\(g_{i, u}\) 表示在第 \(i\) 秒到 \(u\) 号结点的额外收益,那么转移就是
但是这个东西普通的矩乘貌似不能做,因为转移是 \(\max\) 形式的。首先考虑这样的 \(\max\) 转移是否具有结合律。
定义矩阵的 \(\oplus\) 操作(即上面的转移形式)为
令 \(A, B, C\) 为三个矩阵,有
所以这里的结合律依然存在,快速幂依然有用。但是还有一个问题我们没有处理——特殊情况。因为 \(k \le 200\),我们可以把特殊情况按照 \(t\) 排序,乘到一个 \(t\) 就停下来将对应的值加上额外收益,再继续乘即可,但这样的时间复杂度 \(\mathcal{O}((5n)^3k \log T)\),有点爆炸。
设转移矩阵为 \(T\),DP 矩阵为 \(F\)。发现我们每一次都是先乘好 \(T\) 的幂次再把 \(F\) 和它乘,但事实上我们可以预处理出 \(T\) 的 \(2^i\) 次幂,每次都拿 \(F\) 和 \(T\) 的 \(2^i\) 次幂乘,因为 \(F\) 和 \(T^{2^i}\) 的一次乘法是 \(\mathcal{O}((5n)^2)\) 的,所以可以将复杂度降至 \(\mathcal{O}((5n)^3 \log T + (5n)^2k \log T)\),可以接受。
NOI2019 D1T1 回家路线
Description
有 \(n\) 个点,你在第 \(0\) 时刻位于第一个点,你需要走到第 \(n\) 个点。有 \(m\) 班列车,第 \(i\) 班列车在时刻 \(p_i\) 从点 \(x_i\) 出发,在时刻 \(q_i\) 到达点 \(y_i\),只能在时刻 \(p_i\) 上车、时刻 \(q_i\) 下车。给定函数 \(f(x) = Ax^2 + Bx + C\),设 \(s_1, \cdots, s_k\) 为一个合法的回家列车序列,定义这个序列的代价为
求所有合法回家列车序列的最小代价。
原题数据范围:\(n \le 10^5, m \le 2 \times 10^5, A \le 10, B, C \le 10^6, 0 \le p_i < q_i \le 10^3\)。
加强版数据范围:\(n \le 10^5, m \le 10^6, A \le 10, B, C \le 10^7, 0 \le p_i < q_i \le 4 \times 10^4\)。
Solution
预处理出从每个点出发和到每个点的列车,把所有列车按照 \(q\) 排序,设 \(f_i\) 为乘坐第 \(i\) 号列车时的最小代价,那么转移显然,这样暴力做就能拿到 \(70\) 分。
一个想法是斜率优化,但是发现能够转移的点并不连续,所以不能直接莽斜率优化。我们可以对 \(p\) 的值域分块,但这样还是过不了加强版。不过直接把所有 \(p_i\) 的询问离线即可,换句话说就是把一条边拆成加数和查询两个操作,可以直接按照时间排序。时间复杂度 \(\mathcal{O}(m \log m)\)。
注意斜率优化时判横坐标相等!!!
NOI2018 D1T1 归程
Description
给定一张 \(n\) 个点和 \(m\) 条边的图,每条边有一个长度和一个权值。有 \(q\) 组询问,每组询问给定两个数 \(u\) 和 \(p\)。对于一条 \(u \to 1\) 的路径,设 \(v\) 为路径上从 \(u\) 开始第一条权值不大于 \(p\) 的边离 \(u\) 较远的那个结点(若不存在这样的边则令 \(v \leftarrow 1\)),定义这条路径的代价为 \(v \to 1\) 路径上边的长度和,询问 \(u \to 1\) 所有路径的最小代价。
强制在线,\(n \le 2 \times 10^5, m \le 4 \times 10^5, q \le 4 \times 10^5\)。
Solution
- 关于 \(\rm SPFA\)
- 它死了
首先我们需要知道一个叫做 Kruskal 重构树的东西。
Kruskal 重构树是一棵部分点有点权的树。它的构造方法如下:在 Kruskal 算法进行的过程中,对于加入的第 \(i\) 条边,新建一个编号为 \(n + i\) 的结点,它的两条边连向合并的两个集合的根,并将它的点权设为当前这条边的长度,然后将新节点设为根。不难发现最后形成了一个二叉树的结构,我们就把这棵树称为 Kruskal 重构树。
那么 Kruskal 重构树有什么性质呢?不难发现,最小生成树上两个结点简单路径上的最大权值就是他们 LCA 的点权。
回到原题。不难发现可以找到至少一条答案路径在原图的权值最大生成树上,结合上面的性质,发现所有符合要求的点就是以 \(u\) 的所有祖先中权值深度最小的权值大于 \(p\) 的结点的子树。于是我们可以预处理出子树内部的点到 \(1\) 号点的 \(\min\) 值、找到那个点可以树上倍增。
时间复杂度 \(\mathcal{O}(n \log n)\)(默认 \(n, m, q\) 同阶)。
NOI2017 D1T1 整数
Description
有一个整数 \(x\),刚开始为 \(0\),你需要支持两种操作:
- 给定整数 \(a\) 和非负整数 \(b\),令 \(x \leftarrow x + a \cdot 2^b\);
- 给定整数 \(k\),询问 \(x\) 二进制下代表 \(2^k\) 的一位的值。
\(n \le 10^6, |a| \le 10^9, b, k \le 30n\),保证任何时刻 \(x\) 非负。
Solution
一个想法是用 bitset
之类的维护 \(x\) 这个数,但是因为 \(a\) 可能为负,这个算法的时间复杂度会退化到 \(\mathcal{O}(\frac{k^2 \log |a|}{\omega})\)。
发现修改是区间赋值,又看到 \(30n\) 这个奇妙的东西,可以压位上线段树,时间复杂度 \(\mathcal{O}(n \log n \log a_i)\),貌似可以艹过去。