做题计划 9 月

前言

我觉得刷题是可以看题解的,每天刷 \(4\sim 5\) 个偏难的题,主题在图论、DP、计数、DS、杂题中循环选取。

考虑到我启动阶段做题效率还比较低,有模拟赛的至少 \(2\) 题,没模拟赛的改题 + 至少 \(4\) 题。

做题记录

26.9.7

[ICPC 2018 Jakarta R] Rotating Gears

9.8

[OOI 2026] Tasks from Sasha

[JOIST 2025] 迁移计划 / Migration Plan

题目描述

一棵 \(n\) 个点的内向树,\(q\) 次操作,\(3\) 种操作:

  1. 所有深度为 \(x\) 的点,将自己的权值转移沿着边走到能到达的深度为 \(y\) 的点,后文简写为 \(x\to y\)。

  2. 将 \(x\) 点的权值加 \(y\)。

  3. 询问 \(x\) 点的权值。

做法1

\(\mathcal O(n\log^2 n)\),每个深度维护一个 multiset 存 \((fi_u,w_u)\),\(fi_u,se_u\) 分别表示点 \(u\) 在欧拉序中第一、第二次出现的时间戳,\(w_u\) 是点 \(u\) 的权值。

点 \(u\) 是点 \(v\) 的祖先当且仅当 \([fi_v,se_v]\subset[fi_u,se_u]\),设某次操作一 \(a\to b\),那么 \(y\) 能转移到 \(x\) 当且仅当 \(d_y=a\wedge d_x=b\wedge [fi_y,se_y]\in[fi_x,se_x]\)。因为相同深度的点互相没有祖先后代关系,即欧拉序区间交集为空,根据这些性质,做法如下:

  1. 将 \(x\) 深度的可重集合并到 \(y\) 深度的可重集,具体地,将 \(s_x\) 中的所有点加入 \(s_y\) 中,清空 \(s_x\)。为了保证复杂度,让少的合并到大的,启发式合并。

  2. 往 \(s_{d_x}\) 加入 \((fi_x,y)\)。

  3. \([fi_x,se_x]\) 包含的点在 \(s_{d_x}\) 中是一段连续的区间,二分找到区间,遍历求和,删去这段区间,替换为 \((fi_x,ans)\)。

总点数不会超过 \(n+q\),启发式合并的复杂度是对的,询问遍历求和时遍历的总点数也不会超过 \(n+q\)。

9.9

P14447 [ICPC 2025 Xi'an R] Azalea Garden

9.10

P3441 [POI 2006] MET-Subway

9.11

P7880 [Ynoi2006] rldcot

题目描述

有一棵 \(n\) 个节点的树,边有边权。有 \(q\) 次询问,每次询问一个区间 \([l,r]\),求所有 \(i,j\in[l,r]\),本质不同的 \(dep_{LCA(i,j)}\) 的个数。

做法

直接枚举 \((i,j)\),时间爆炸。发现有很多点对是没有用的,对于 \((a,b),(c,d),a\leq c,d\leq b\) 如果 \(LCA(a,b)=LCA(c,d)\),那么 \((a,b)\) 是没有用的,称 \((a,b)\) 包含 \((c,d)\)。现在考虑删掉只保留有用的点对,枚举点 \(u\) 是有用点对的 LCA,那就是在点 \(u\) 的不同子树中找两个点,这个点对不包含其他点对。对于 \(u\) 子树内的某个点 \(a\),为了能在子树中找到另一个点 \(b\),组成的点对不包含其他点对,\(b\) 至少是小于 \(a\) 的最大的(前驱),或者是大于 \(a\) 的最小的(后继)。如果直接这样子做是不对的,考虑毛毛虫树,这样点对数量还是 \(\mathcal O(n^2)\) 级别的。发现 \(a\) 并不需要在 \(u\) 的整个子树中找前前驱后继,而是可以每次加一个儿子的子树,对于这个子树的点,只需要在之前加入的子树中找前驱后继就行了。考虑证明:如果点 \(a\) 所在的子树加入时,它真正的后继 \(b\) 还没有加入,那么此时 \(a\) 找前面的子树组成的点对显然是不优的。但是 \(b\) 匹配时一定能找到不劣于 \((a,b)\) 的点对,所以这样是对的。既然可以自由决定加入子树的顺序,考虑树上启发式合并,对于每个 \(u\),除了第一个子树,它的每个子树的点都要找一个前驱和一个后继。每次保留 \(u\) 的重儿子,那相当于对每个轻儿子加一个子树大小,也就是对每个点加上其到根的路径上轻边的数量,这个数量是 \(\mathcal O(n\log n)\) 级别的。

最终会有 \(\mathcal O(n\log n)\) 级别的点对,询问就是区间包含的点对数颜色。考虑将询问对左端点排序从大到小扫描线,扫到 \(i\) 是加入所有左端点大于等于 \(i\) 的点对,在点对的右端点处加入该点的颜色(LCA 深度),每次询问查询区间内本质不同的颜色数。为了不算重,记录每个颜色出现的最小位置,每种颜色只在最小位置被统计。

能用树状数组就不要用线段树!

9.13

此花绽放之时

题目描述

有一棵树,\(n\) 个节点,点有颜色,有三种操作:

  1. 修改 \(u,v\) 间路径上点的颜色。

  2. 将点 \(u\) 所在的极大同色连通块中的点点权增加 \(v\)。

  3. 查询点 \(u\) 的点权。

做法

连通块加找到这个连通块最浅的一个点,因为不能直接遍历,考虑在这个点上打标记,打上 \((t,c,v)\) 代表时间、颜色、增加值,但标记有三个参数,无法合并。考虑删掉时间,为了保证正确性操作一要将 \(u\) 到根路径上的点的标记下传给所有儿子,操作三要将 \(u,v\) 到根的路径下传。但是这样复杂度太高,发现是对点到根的路径上的点进行操作,考虑重链剖分优化。对所有被 pushdown 的节点,将标记下传给重链上的点,这样操作一三路径上的点只需要下传标记给轻儿子,但是轻儿子的个数很多,考虑标记永久化,要用的时候再加上标记的贡献,这样一条路径上就只有 \(\mathcal O(\log n)\) 个节点需要 pushdown,为了避免重复统计标记,要开一个数组对每个链顶(pushdown 时接收了标记值的轻儿子)记录其父亲下传到这个点的值,这一次的减去上一次的才是真正应该增加的。在操作一的时候,由于 \((u,v)\) 路径上的点颜色都变了,要将路径上的轻儿子的去重数组变成新颜色的标记值,因为这些标记在这个点变成这个颜色之前,这个点是接收不到这个标记的。结合代码能更好理解。

26.9.21

P6782 [Ynoi2008] rplexq

题目描述
做法

牛牛。先容斥,LCA 是 \(u\) 的点对等于 \(u\) 子树中的点对数量减 \(u\) 儿子的子树中的点对数量的和。然后根据儿子个数的多少平衡两种做法,一种是将子树转为两个 dfn 序前缀的差,扫描线统计点数,再用每个子树的点数乘其前面的子树的点数。第二种是莫队数区间的每种颜色的个数。

还有一个很妙的证明,但是好像类似于树剖的重儿子的证法。是证明所有点不选前 \(B\) 小的子树,其他子树节点数和是 \(\mathcal O(n\log_4n)\) 级别的,好像就是一样的。

CF1270G Subset with Zero Sum

题目描述
做法

令 \(b_i=i-a_i\),则 \(1\leq a_i\leq n\),将 \(i\) 与 \(b_i\) 连边,发现一定有环,而一个环的 \(i-b_i\) 之和为零(一条边 \((u,v)\),由于 \(v=b_u\),所以 \(u-b_u+v-b_v=u-b_v\),环首尾相连刚好全消完了)。

反思

有往图论建模想,但是没有想到转化为 \(b\) 数组。这种范围刚好错开的可以与下标相减使范围重合。

CF1720E Misha and Paintings

题目描述
做法

令目前有 \(m\) 种颜色。

  1. 若 \(m\leq k\),每次修改 1×1 的矩阵增加一种颜色,修改次数最少是 \(k-m\)。

  2. 若 \(m>k\),让一个正方形紧贴原矩阵左、上边界,颜色与原矩阵右下角相同,保证 \(m'\geq k\),一直拓展这个正方形直到无法拓展,设最终边长为 \(L\)。再用一个正方形,颜色与第一个正方形一样,右下角坐标为 \((L+1,L+1)\),即位于从上往下数第 \(L+1\) 行,从左往右数第 \(L+1\) 列的格子。将第二个正方形向左上方拓展,发现每次最多减少两种颜色,而修改第二个正方形的颜色可以多一种颜色,这样就一定能凑齐 \(k\) 种颜色。所以最多用两个正方形就能使原矩阵的颜色数修改为 \(k\) 种。
    但是如何判断是否只用一种颜色就可以呢?考虑枚举这个正方形的长度 \(L\),我们用某个正方形的左上角来代表这个正方形。要删掉一种颜色就是要这种颜色的格子被完全覆盖,对每种颜色统计上下左右边界:\(a,b,c,d\),那么完全覆盖这种颜色的正方形需要满足:

\[\begin{aligned} i&\leq a\\ i+len-1&\geq b\\ j&\leq c\\ j+len-1&\geq d \end{aligned} \]

即

\[\begin{aligned} b-len+1\leq i\leq a\\ d-len+1\leq j\leq c \end{aligned} \]

满足条件的 \((i,j)\) 是一个矩形,每个 \(len\) 统计答案前二维前缀预处理。注意某个正方形完全覆盖的颜色数等于 \(m-k\) 或 \(m-k+1\) 都可以。

9.25

P6779 [Ynoi2009] rla1rmdq

9.26

P5361 [SDOI2019] 热闹的聚会与尴尬的聚会

9.28(贪心专题)

P14880 [ICPC 2019 Yokohama R] Halting Problem(并不是贪心,模拟赛改题)

exchang argument

交换论证是证明贪心的最常用最好用的方法之一。

  1. 将贪心解与某个最优解交换元素,比如:

    安排一些任务,每个任务需要一个时间段来完成,两个任务时间不重叠,求能完成的任务的最大数量。

    贪心的做法是将所有任务按结束时间升序排序,每次选择不与已选任务冲突中结束时间最小的任务。

    证明:设最优解 \(OPT=(o_1,o_2,\dots,o_m)\),贪心解 \(G=(g_1,g_2,\dots,g_n)\),取出 \(o_1,g_1\),由于 \(g_1\) 的结束时间最小,将 \(o_1\) 替换成 \(g_1\) 也成立。后面的位置证明同理。假设 \(n<m\),即贪心解不是最优,根据前面的证明,\(g_n\) 的结束时间不会晚于 \(o_n\),所以贪心解还可以继续选 \(o_{n+1}\),和贪心做法矛盾,所以 \(n\geq m\),即 \(n=m\)。

    这个贪心做法的思路是在每一步可行的选择中选择对后面的最有利的。

  2. 邻项交换,适用于贪心构造序列/排列:

    设计贪心策略的过程是这样的:假设 \(S\) 是当前解,交换 \(s_i,s_j\),假设交换后的更优,列出一些不等式,得到按什么关键字排序最优。

    而证明是这样的:假设 \(O\) 是最优解,若 \(O\) 中存在相邻的逆序对(两个下标的关系不符合贪心策略),交换逆序对,证明交换后不会变劣。反复消除逆序对,最终得到一个没有逆序对的最优解,即贪心解,就说明了贪心解也是最优的。

  3. 环交换,最小生成树。

P6927 [ICPC 2016 WF] Swap Space

题目描述

需要更新 \(n\) 个硬盘,第 \(i\) 个硬盘需要占用 \(a_i\) 的空间更新,更新后提供 \(b_i\) 的新空间(占用的不会空出),求需要的初始空间的大小的最小值。

做法

将硬盘分类。

有两个更新操作序列上相邻的硬盘 \((a_1,b_1),(a_2,b_2)\)

  1. \(a<b\)

先 \(1\) 后 \(2\),空间更新的状态是:\(-a_1+b_1-a_2+b_2\),最大需要的空间等于 \(max(a_1,a_1-b_1+a_2)\)。

交换 \(1,2\) 后是:\(-a_2+b_2-a_1+b_1\),最大需要的空间是 \(max(a_2,a_2-b_2+a_1)\)。

假设交换后更优。

由于 \(a_1-b_1+a_2<a_2\),那么可以把交换前的最大空间直接写成等于 \(a_1\),因为它不能等于 \(a_1-b_1+a_2\),否则就优于交换后了。

由于 \(a_2-b_2+a_1<a_1\),那么交换后的最大空间可以直接写成等于 \(a_2\),因为 \(a_2-b_2+a_1\) 已经更优,要判断哪个更优只取决于 \(a_1,a_2\) 的关系。

而交换后更优,所以 \(a_2\leq a_1\),也就是按 \(a\) 升序选择这类硬盘是最优的。

  1. \(a>b\)

先 \(1\) 后 \(2\) 是:\(-a_1+b_1-a_2+b_2\),最大需要的空间是 \(max(a_1,a_1-b_1+a_2)\)。

交换后是:\(-a_2+b_2-a_1+b_1\),最大需要 \(max(a_2,a_2-b_2+a_1)\) 的空间。

按 \(b\) 降序选择最优,证明同 \(a<b\) 的情况。

  1. \(a=b\)

在空间最大的时候更新这类硬盘即可。

每一类内部的顺序已经决定,现在考虑前两类之间是什么顺序。发现第一类在加可用空间,而第二类在减空间,所以肯定是 \(1\to 3\to 2\) 的顺序更新每一类是最优的。

AT_dp_x Tower

题目描述

\(n\) 个箱子,第 \(i\) 个箱子有重量 \(w_i\)、承重能力 \(s_i\)、价值 \(v_i\)。选出一个箱子子集并确定顺序,小下标的箱子放在大下标的箱子上。求选出的箱子的价值和的最大值。

做法

子集不好 dp,但是将箱子最优排序后可以子序列 dp,考虑以什么方式排序。

假设当前箱子序列中某个相邻的两个箱子为 \(i,j\),\(i\) 前面箱子的重量和为 \(S\)。

考虑交换有什么变化,发现对前后都没有影响。

交换前需满足:

\[\begin{aligned} S\leq s_i\\ S+w_i\leq s_j \end{aligned} \]

即

\[S\leq \min(s_i,s_j-w_i) \]

交换后需满足:

\[\begin{aligned} S\leq s_j\\ S+w_j\leq s_i \end{aligned} \]

即

\[S\leq \min(s_j,s_i-w_j) \]

假设交换后箱子也同样能够承受重量,即 \(\min(s_j,s_i-w_j)/geq \min(s_i,s_j-w_i)\),和上一题一样的证法。

由于 \(s_i-w_j<s_i\),所以交换前的最大值不能等于 \(s_i\),否则交换后就不优了。

由于 \(s_j>s_j-w_i\),所以 \(s_j\) 对交换前后的大小没有影响。

因此比较 \(s_i-w_j\) 和 \(s_j-w_i\),由于交换后更优,所以 \(s_i-w_j>s_j-w_i\),即 \(s_i+w_i>s_j+w_j\),即按 \(s_i+w_i\) 升序排序最优。

然后就是背包 dp。

AT_agc023_f [AGC023F] 01 on Tree

很牛的题啊,去年今日做的,做法还记得一半。

题目描述

\(n\) 个点的外向树,点有 \(0/1\) 点权,求树的所有拓扑排序中,点权形成的逆序对的最小个数。

做法

首先选完一个点之后,它的儿子如果有 \(0\),直接紧接着选肯定是最优的,可以反证,假设先选了 \(1\),那么把 \(0\) 移到前面,逆序对一定会减少。

那么一个点权为 \(0\) 的点就和父亲绑定了(最终拓扑序中,这个点一定紧靠在父亲后面),将它和父亲缩成一个点。考虑继续缩剩下的点,发现每个点有两个参数:\(cnt0,cnt1\) 表示这个点代表原树上几个 \(0/1\) 点,(发现前面缩的 \(0\) 就是 \(cnt1=0\) 的特殊情况,那好像就能直接猜出按 \(\dfrac{cnt0}{cnt1}\) 降序的顺序缩点,😮),对与拓扑序上相邻的两个点 \(u,v\)(\(u\) 在 \(v\) 前),尝试交换。

交换前后,变化是 \(-u1v0+v1u0\),这个值小于 \(0\) 的时候交换更优,可以推出 \(\dfrac{v1}{v0}<\dfrac{u1}{u0}\) 时交换更优(两边取倒数就是上面说的结论)。但是显然不能直接排序,考虑找到 \(\dfrac{cnt1}{cnt0}\) 最小的点,那么当它的父亲被选择的时候,它将会是它父亲子树中最先选择的点,那么它和它父亲在拓扑序上就连在一起了,可以缩成一个点。

实现用并查集和优先队列。

9.29

P7831 [CCO 2021] Travelling Merchant

卧槽好牛

题目描述

\(n\) 个点 \(m\) 条边的 有向 图,第 \(i\) 条边从 \(u_i\) 到 \(v_i\),资产不少于 \(r_i\) 才能经过这条道路,经过之后会获得 \(p_i\) 元。对每个点,以这个点作为出发点,求出至少需要多少初始资产才能永远走下去。

做法

首先这是有向图,走到一个环上就可以永远走下去,那么先删掉到不了环上的点,然后考虑 dp。

设 \(f_u\) 表示从点 \(u\) 出发,能永远走下去的最小初始资产。当一个点 \(v\) 的出度为 \(0\),那么它的 dp 值已经确定了,那么它就可以沿反向边更新其他的点并删掉这条边,比如对于边 \((u,v)\),更新 \(f_u\leftarrow min(f_u,max(r,f_v-p))\) 并删除 \((u,v)\),含义就是经过这条边的限制和加上这条边的补给之后到环的最小资产。

但是会出现所有点的 dp 值都未确定的情况,怎么办呢?这是就可以找到当前为删除的边中边权最大的边,转移 \(f_u\leftarrow min(f_u,r)\),删除。为什么是对的呢?从 \(u\) 以 \(r\) 的初始资产出发,由于当前图上所有边的边权都小于 \(r\),所以 \(u\) 可以走遍剩下的图,而且一定能走到环,所以可以和 \(r\) 取较小值,而 \(v\) 转移过来的最小值也是 \(r\)。

AT_arc098_d [ARC098F] Donation

卧槽这么牛

题目描述

\(n\) 个点,\(m\) 条边的无向图,第 \(i\) 个点需要在钱数不小于 \(a_i\) 时才能通过,在这个点上时你可以选择捐款。你需要选择从一个点出发,并在每个点恰好捐款一次。求最小初始钱数。

做法

如果多次经过一个点,肯定是在最后一次经过这个点的时候捐款,如果不是在最后一次捐款,可以移到最后一次,而中间的钱数会变多,这显然是更优的。正着做不好做,因为不知道当前经过的这个点未来会不会再次经过。

考虑倒着做,将问题转化成:选择从一个点出发,拥有 \(c_u=a_u-b_u\) 元的时候才能经过点 \(u\),第一次访问的时候将钱数加上 \(b_u\)。求能够遍历每个点的最小初始钱数。

发现如果当前在点 \(u\),只保留点权小于等于 \(c_u\) 的点,\(u\) 可以走到其所在连通块内的任意点。在有点权限制的情况下,求一个点可达的所有点的 \(b\) 之和,可以想到先将点权转边权(\(w_{u,v}=max(c_u,c_v)\),再建立 kruskal 重构树。在重构树上,到达一个点,就能获得子树中所有点的点权,再尝试往上跳,如果钱不够到达父亲,就增加初始钱数。由于要求所有叶子到根的最小初始钱数,有两种做法:可以自底向上 dp,也可以自顶向下直接修改每个点。

讲一下自底向上的 dp:设 \(f_u\) 表示从 \(u\) 子树中的叶子出发,到达 \(u\) 需要的初始钱数的最小值。转移就是:

\[f_u=\min_{v\in son_u}(f_v+max(0,c_u-f_v-siz_v)) \]

\(siz_u\) 表示 \(u\) 子树中叶子节点 \(b\) 之和。转移的意义是如果从 \(v\) 转移来,一定会经过 \(v\),为了满足能到达 \(v\),\(f_u\) 至少是 \(f_v\)。但是初始钱数为 \(f_v\) 可能无法到达 \(u\),考虑贪心地将 \(v\) 中的所有点选上,如果这都不够就只能增加初始钱数了。

P5633 最小度限制生成树 - 洛谷

题目描述

一个 \(n\) 个点,\(m\) 条边的带边权无向图,求一棵生成树,要求节点 \(s\) 在生成树中恰好与 \(k\) 条边相连,输出生成树边权和的最小值。

做法

考虑最终的生成树删掉 \(s\) 之后是什么样的,一定是有若干个连通块而且每个连通块恰好有一个点在删 \(s\) 之前与 \(s\) 相连。回到开始,将 \(s\) 删掉,先求出最小生成森林(注意生成森林的定义是每个连通块的生成树组成的森林),考虑贪心地在每个联通块中选一个与 \(s\) 相连且边权最小的点,连接这个点和 \(s\)。但是现在可能还需要再给 \(s\) 连几条边,新连一条边之后会形成环,要删掉环上不与 \(s\) 相邻的边中边权最大的。给每个点赋一个点权 \(a_u=b_u-val_u\),\(b_u\) 等于 \(u\) 与 \(s\) 相连的边的边权,没有连边就是正无穷。\(val_u\) 表示 \(u\) 与其所在联通块中与 \(s\) 连了边的点的路径上边权的最大值。取最小的点。

posted @ 2026-09-14 07:41  Tanliu  阅读(17)  评论(0)    收藏  举报