写这篇文章的原因是在做 P7246 时被题解深深地恶心到了。
今天我决定把他们按难度一一拆解!
| 形状 \ 品种 | 连续 | 跳跃 | 循环 | 跳跃循环 |
|---|---|---|---|---|
| 链 | 连续链铲雪 | 跳跃链铲雪 | 循环链铲雪 | 跳跃循环链铲雪 |
| 环 | 连续环铲雪 | 跳跃环铲雪 | 循环环铲雪 | 跳跃循环环铲雪 |
| 树 | 连续树铲雪 | 跳跃树铲雪 | 循环树铲雪 | 跳跃循环树铲雪 |
好像似曾相识:
| 时间 \ 形态 | 一般 | 进行 | 完成 | 进行完成 |
|---|---|---|---|---|
| 现在 | 一般现在时 | 现在进行时 | 现在完成时 | 现在进行完成时 |
| 过去 | 一般过去时 | 过去进行时 | 过去完成时 | 过去进行完成时 |
| 将来 | 一般将来时 | 将来进行时 | 将来完成时 | 将来进行完成时 |
| 过去将来 | 一般过去将来时 | 过去将来进行时 | 过去将来完成时 | 过去将来进行完成时 |
原来铲雪是英语时态!啊!我不活了!
啊!你要铲雪!我要吐血!
Oh No,我无疑是愤怒的!!!
不不不!和时态不对仗我再来加 \(4\) 个。
基环树铲雪、单点清空铲雪、树上双叶铲雪、二元铲雪。
嘿嘿嘿(邪恶)。
铲雪是什么?
铲雪的格式类似于这样:
给你一个"形状",每次可以选择一个"形状",把这个"形状"经过的点的点权都减去 \(1\),要求无论何时都不能减到负数,问最少操作次数。
真的,看上去简单实际上难。
连续链铲雪
差分法
哦,好吧其实这个铲雪是一道黄题,你不信?看这个 P1969。
很水吧。
这一类铲雪甚至都不需要 DP!
直接差分即可,注意查分的结果要取 MAX。
:::info正确性证明
发现有文章嫌他太简单直接跳过了,我还是来讲一讲吧。
我们知道一个数组绝对可以拆分成几个连续上升子序列。
OK,我们来一张图。

发现他真的有可差分性,于是差不多就整完了,上面的图应该很清楚了。
:::
笛卡尔树版
笛卡尔树是啥?
对序列建一棵小根笛卡尔树:
- 中序遍历是原下标顺序。
- 每个节点的值是子树区间里的最小值。
- 根是整个区间的最小值位置。
比如序列:2, 3, 4, 1, 2。
全局最小值 \(1\) 在位置 \(4\),所以位置 \(4\) 是根。
左边 \(1\sim 3\) 是左子树,右边 \(5\sim 5\) 是右子树。
怎么做?
做法:从根开始递归。
每个节点代表一个区间,节点的值就是这个区间的最低高度。
核心思想
每一层先把整个区间铲到最低高度,然后递归处理左右两边。
具体步骤
从根节点开始,传入一个参数 \(base\),表示 "当前这个区间已经被铲掉了多少层"。
初始 \(base = 0\)。
对于当前节点(代表区间 \(l\sim r\),最小值为 \(h\)):
- 先铲 \(h-base\) 次:把整个区间从当前高度 \(base\) 铲到最低高度 \(h\)。这 \(h-base\) 次操作覆盖整个区间。
- 递归左子树:左子树的区间现在基准高度变成了 \(h\),继续处理。
- 递归右子树:同理。
把这三部分的次数加起来。
:::info例子演示
初始 base = 0。
- 根节点:位置 \(4\),值 \(=1\),区间 \(=1\sim 5\)
- \(base=0\),\(h=1\)。
- 铲 \(1-0=1\) 次,覆盖整个 \(1\sim 5\)。
- 累计答案:\(1\)。
- 递归左子树 \(1\sim 3\),\(base=1\)。
- 递归右子树 \(5\sim 5\),\(base=1\)。
- 左子树根:位置 \(1\),值 \(=2\),区间 \(=1\sim 3\)
- \(base=1\),\(h=2\)。
- 铲 \(2-1=1\) 次,覆盖 \(1\sim 3\)。
- 累计答案:\(1+1=2\)。
- 左子树为空,跳过。
- 递归右子树 \(2\sim 3\),\(base=2\)。
- 节点:位置 \(2\),值 \(=3\),区间 \(=2\sim 3\)
- \(base=2\),\(h=3\)。
- 铲 \(3-2=1\) 次,覆盖 \(2\sim 3\)。
- 累计答案:\(2+1=3\)。
- 左子树为空,跳过。
- 递归右子树 \(3\sim 3\),\(base=3\)。
- 节点:位置 \(3\),值 \(=4\),区间 \(=3\sim 3\)
- \(base=3\),\(h=4\)。
- 铲 \(4-3=1\) 次,覆盖 \(3\sim 3\)。
- 累计答案:\(3+1=4\)。
- 左右子树均为空,返回。
- 右子树:位置 \(5\),值 \(=2\),区间 \(=5\sim 5\)
- \(base=1\),\(h=2\)。
- 铲 \(2-1=1\) 次,覆盖 \(5\sim 5\)。
- 累计答案:\(4+1=5\)。
- 左右子树均为空,返回。
最终答案:5。
:::
线性规划与对偶法
不好意思,我是真的不知道怎么避免出现式子了。
原始问题:用区间操作凑出序列
设变量 \(x_{l,r}\) 表示区间 \(l,r\) 被操作的次数。
目标是最小化总操作次数:
\\\min \\sum_{1\\le l\\le r\\le 5} x_{\[l,r} \]
约束是每个位置最终恰好等于目标高度:
\\\sum_{l\\le i\\le r} x_{\[l,r} = a_i,\quad \forall i \]
\x_{\[l,r}\ge 0 \]
例如位置 \(3\) 的约束是:
\\\sum_{\[l,r\ni 3} x_{l,r} = 4 \]
意思是所有覆盖位置 \(3\) 的区间操作次数之和必须等于 \(4\)。
对偶问题:给每个位置分配权重
对每个位置 \(i\) 引入对偶变量 \(y_i\),表示给位置 \(i\) 分配的"权重"。
对偶问题是:
\\\max \\sum_{i=1}\^5 a_i y_i \\
约束是:
\\\sum_{i=l}\^r y_i \\le 1,\\quad \\forall\\, 1\\le l\\le r\\le 5 \\
意思是:任意连续区间的权重之和不能超过 \(1\)。
为什么对偶是下界
每次操作覆盖一个连续区间 \(l,r\),而这个区间的权重和 \(\sum_{i=l}^r y_i \le 1\)。
所以每次操作最多贡献 \(1\) 的加权和。
那么所有操作的总贡献不超过操作次数。
而所有位置的总加权和是 \(\sum a_i y_i\),它必须被这些操作"支付"。
因此:
\\\text{操作次数} \\ge \\sum_{i=1}\^5 a_i y_i \\
对偶就是在所有合法权重分配中,找最大的加权和,得到最紧的下界。
前缀和变换
令:
\S_0=0,\\quad S_k=\\sum_{i=1}\^k y_i \\
那么区间约束变成:
\S_r - S_{l-1} \\le 1,\\quad \\forall\\, 0\\le l-1\
即:
\\\max S_i - \\min S_i \\le 1 \\
前缀和序列的波动幅度不能超过 \(1\)。
目标函数用分部求和改写:
\\\sum_{i=1}\^5 a_i y_i = \\sum_{i=1}\^5 (a_i - a_{i-1}) S_i,\\quad a_0=0 \\
代入示例序列 \(a = 2,3,4,1,2\):
\(2-0)S_1 + (3-2)S_2 + (4-3)S_3 + (1-4)S_4 + (2-1)S_5 \\
\= 2S_1 + 1S_2 + 1S_3 - 3S_4 + 1S_5 \\
最优权重分配
由于目标是线性的,最优解一定在边界上。可以设 \(\min S_i = 0\),\(\max S_i = 1\),于是每个 \(S_i \in \{0,1\}\)。
- 当 \(a_i - a_{i-1} > 0\) 时,\(S_i\) 取 \(1\) 更优;
- 当 \(a_i - a_{i-1} < 0\) 时,\(S_i\) 取 \(0\) 更优;
- 当 \(a_i - a_{i-1} = 0\) 时,取什么都一样。
代入示例:
| \(i\) | \(a_i - a_{i-1}\) | 最优 \(S_i\) | 贡献 |
|---|---|---|---|
| 1 | \(2\) | \(1\) | \(2\) |
| 2 | \(1\) | \(1\) | \(1\) |
| 3 | \(1\) | \(1\) | \(1\) |
| 4 | \(-3\) | \(0\) | \(0\) |
| 5 | \(1\) | \(1\) | \(1\) |
对偶最优值:\(2+1+1+0+1 = 5\)。
强对偶闭合
原始问题有可行解(笛卡尔树给出了一个方案,操作次数为 \(5\)),对偶问题也有可行解,强对偶成立。
因此:
\\\boxed{\\min \\text{操作次数} = \\sum_{i=1}\^n \\max(0, a_i - a_{i-1})} \\
代入示例:\(\max(0,2)+\max(0,1)+\max(0,1)+\max(0,-3)+\max(0,1) = 2+1+1+0+1 = 5\)。
没有其他的了吧?qwq
不!还有带限制的!!!
noooooooo!!! 为什么学个最普通的铲雪还有这么多,我已急哭。-
限制连续链铲雪
其实就是规定只能铲长度为 \(l\sim r\) 的雪。
为了方便设(其实不这样无解):\(r-l+1\ge \lfloor\frac{n}{2}\rfloor\)。
转换问题:
放一条从 \(L\) 到 \(R\) 的 "射线"。
每个位置 \(i\) 恰好被 \(a_i\) 条射线覆盖。
最少射线总数。
先暂时忽略上界 \(r\),只要求每条射线长度 \(\ge l\)。
思路和基础铲雪一样,但要额外检查合法性。
情况一:\(a_i > a_{i-1}\)(需要新开射线)
在位置 \(i\) 新开 \(a_i - a_{i-1}\) 条射线。这些射线的起点是 \(i\),最短也要延伸到 \(i+l-1\)。
\\\text{若 } i + l - 1 \> n \\implies \\text{无解} \\
因为射线还没达到最小长度 \(l\) 就出界了。
情况二:\(a_i < a_{i-1}\)(需要截断射线)
需要在位置 \(i-1\) 处截断 \(a_{i-1} - a_i\) 条射线。但不是所有射线都能截断 ------ 只有那些已经延伸了至少 \(l\) 长度的射线才能被截断。
为什么?因为每条射线长度必须 \(\ge l\)。如果一条射线从 \(s\) 出发,当前在位置 \(i-1\),它的长度是 \((i-1) - s + 1 = i - s\)。只有 \(i - s \ge l\) 即 \(s \le i - l\) 时,才能在这里截断。
所以我们需要知道:有多少条已开始的射线,其起点 \(\le i-l\) 且尚未被截断? 如果这个数量 \(< a_{i-1} - a_i\),则无解。
如何维护可截断的射线?
用一个队列(或计数器)维护所有活跃的射线,按起点时间排序。队头是最早开始的射线(当前长度最长),队尾是最近开始的射线(当前长度最短)。
当需要截断时,从队头取出射线截断 ------ 因为队头的射线最长,最可能已经满足 \(\ge l\) 的条件。
加上上界 \(r\)。
现在要求每条射线长度 \(\in l, r\)。最直接的思路是:当一条射线长度达到 \(r\) 时,必须截断它。但如果此时位置 \(i\) 还需要继续覆盖怎么办?就得新开一条射线。这会增加答案吗?
核心性质
保证:\(r - l + 1 \ge \left\lceil \frac{n}{2} \right\rceil\)
这个条件非常强。 它意味着区间 \(l, r\) 的宽度至少是 \(n\) 的一半。
任何 \(x \in (r, n]\) 都能拆成两个 \(l,r\) 内的数之和
证明 :设 \(x > r\),我们要找 \(p, q \in l, r\) 使得 \(p + q = x\)。
取 \(p = l\),则 \(q = x - l\)。需要验证 \(q \in l, r\):
- \(q \ge l \iff x - l \ge l \iff x \ge 2l\)。由于 \(x > r \ge l\) 且 \(r - l + 1 \ge \lceil n/2 \rceil \ge l\)(当 \(n \ge 2l-1\) 时),这个条件在题目约束下通常成立。更严谨地:
取 \(p = x - r\),则 \(q = r\)。需要 \(p \in l, r\):
- \(p \le r \iff x - r \le r \iff x \le 2r\),显然成立(\(x \le n \le 2r\) 由条件可推)。
- \(p \ge l \iff x - r \ge l \iff x \ge l + r\)。
如果 \(x \ge l + r\),取 \(p = x-r \ge l\),\(q = r\),合法。
如果 \(x < l + r\),取 \(p = l\),\(q = x - l < r\),且 \(q \ge x - r + (r-l) \ge \cdots\) 需要 \(x - l \ge l\) 即 \(x \ge 2l\)。
实际上更简单的论证:\(l,r\) 中任意两数之和的范围是 \(2l, 2r\)。由 \(r - l + 1 \ge \lceil n/2 \rceil\) 可得 \(2r \ge n + l - 1 \ge n\),且 \(2l \le l + r \le r + 1 < n\)(当 \(n\) 较大时)。所以 \(r+1, n \subseteq 2l, 2r\),即任何 \(x \in (r, n]\) 都能写成两个 \(l,r\) 内数之和。
这个推论意味着什么?
假设一条射线从 \(s\) 出发,一直延伸到 \(t\),长度 \(t - s + 1 > r\)。按原始约束这是不合法的。但根据推论,我们可以把这条超长射线拆成两条合法射线:
- 第一条:\(s, s + p - 1\),长度 \(p \in l, r\)
- 第二条:\(s + p, t\),长度 \(q = (t-s+1) - p \in l, r\)
这里的 "拆" 不是真的在算法中拆,而是说 ------ 即使我们在算法中让一条射线延伸超过 \(r\),最终也一定能通过某种方式把它重新组织成若干条合法射线,且总数量不会比我们及时截断的方案更差。
更准确地说:如果我们贪心地尽量不截断 (让射线尽可能延伸),得到的射线数量是最少的(因为每条射线覆盖更多位置,复用率最高)。虽然有些射线可能超长,但利用上述性质,超长部分可以通过 "拆分重组" 变成合法方案,且不会增加射线总数(因为拆分后的射线可以和其他射线合并调整)。
更直观的理解
想象你有若干条射线,有些长度 \(> r\)。由于 \(l,r\) 区间很宽,你可以把这些超长射线拼刀刀砍一刀切一刀,切成两段合法的。切出来的后半段可以和附近其他射线的后半段交换拼接,最终所有射线都合法,且总数不变。
结论 :在最小化射线数量的目标下,长度 \(> r\) 本身不会让答案变差 。我们只需要关心 " 有多少条射线长度 \(> r\)"------ 这个数量越少,后续调整越容易。
不要问我为啥不讲完整的,因为一位不愿意透露姓名的巨佬说要用最小费用流,超纲了!(就算没有超纲也没人愿意听了)
如果需要的话通知我加上。
连续环铲雪
首先记住经验:画环为链。
问题定义
给定环形序列 \(a_1, a_2, \ldots, a_n\)(\(a_{n+1} = a_1\)),每次操作选择环上一段连续区间 减 \(1\),求将所有元素变为 \(0\) 的最少操作次数。
大废话。
记:
\M = \\max_{i=1}\^{n} a_i \\
\D = \\frac{1}{2}\\sum_{i=1}\^{n} \|a_i - a_{i+1}\| = \\sum_{i=1}\^{n} \\max(0,\\ a_{i+1} - a_i) \\
结论:
\\\boxed{\\text{ans} = \\max(M,\\ D)} \\
为什么答案至少是 \(\max(M,\ D)\)
下界一:答案 \(\ge M\)
每次操作最多让每个位置减 \(1\)。位置 \(i\) 初始值为 \(a_i\),至少要被覆盖 \(a_i\) 次。因此:
\\\text{ans} \\ge \\max_i a_i = M \\
这是最直观的下界。
下界二:答案 \(\ge D\)
考虑环上相邻位置的差分。定义:
\\\Delta_i = a_{i+1} - a_i,\\quad i = 1, 2, \\ldots, n \\
其中 \(a_{n+1} = a_1\)。注意到环上差分之和为零:
\\\sum_{i=1}\^{n} \\Delta_i = 0 \\
关键观察 :一次操作选择区间 \(L, R\)(环上连续),将所有位置减 \(1\)。它对差分的影响是:
- 在左端点 \(L\) 处:\(\Delta_{L-1}\) 增加 \(1\)(因为 \(a_L\) 减了 \(1\),而 \(a_{L-1}\) 没变);
- 在右端点 \(R\) 处:\(\Delta_R\) 减少 \(1\)(因为 \(a_{R+1}\) 没变,而 \(a_R\) 减了 \(1\));
- 区间内部差分不变。
因此,每次操作最多让 \(\sum |\Delta_i|\) 减少 \(2\) (当 \(\Delta_{L-1} < 0\) 且 \(\Delta_R > 0\) 时取到最优)。
初始时 \(\sum |\Delta_i| = 2D\),目标状态全为零时 \(\sum |\Delta_i| = 0\)。所以至少需要:
\\\text{ans} \\ge \\frac{2D}{2} = D \\
另一种理解 :\(D = \sum \max(0, \Delta_i)\) 表示环上所有 "上升沿" 的总高度。每次操作最多消除一个上升沿单位,所以至少需要 \(D\) 次操作。
为什么 \(\max(M,\ D)\) 次操作一定够
构造思路
我们证明:总可以用 \(\max(M, D)\) 次操作完成铲雪。
情况一:\(M \ge D\)
此时答案为 \(M\)。构造方法:对每个位置 \(i\),用 \(a_i\) 条长度为 \(1\) 的区间 \(i, i\) 覆盖它。总共 \(\sum a_i\) 次操作......但这不一定等于 \(M\)。
更优的构造:由于 \(M \ge D\),说明序列波动不大。
我们可以这样做:
-
找到最大值位置 \(p\),使得 \(a_p = M\)。
-
从 \(p\) 出发,沿环的两个方向各构造若干条区间,每条区间从 \(p\) 延伸到某个位置 \(q\),长度为 \(a_q\)。
-
由于 \(M \ge D\),这些区间的总数量恰好为 \(M\)。
更严谨的构造 :将环从最大值位置 \(p\) 切开,变成一条链 \(a_p, a_{p+1}, \ldots, a_n, a_1, \ldots, a_{p-1}\),其中首元素为最大值 \(M\)。对这条链使用链上公式:
\\\text{ans}_{\\text{chain}} = \\sum_{i} \\max(0, a_i - a_{i-1}) \\
由于首元素为 \(M\),且 \(M \ge D\),可以证明这个链上答案恰好等于 \(M\)。
情况二:\(D > M\)
此时答案为 \(D\)。
构造方法:
- 将环上所有上升沿提取出来。设上升沿位置集合为 \(\{i \mid a_{i+1} > a_i\}\),每个上升沿的高度为 \(a_{i+1} - a_i\)。
- 对每个上升沿,从位置 \(i+1\) 出发,向左(逆时针)延伸一条区间,长度为 \(a_{i+1} - a_i\),终点为 \(i\)。
- 这些区间互不重叠(在差分意义上),总数量为 \(D\)。
由于 \(D > M\),说明序列波动较大,需要更多操作来 "填平" 上升沿。但每次操作恰好消除一个单位的上升沿,所以 \(D\) 次操作恰好够。
统一构造
实际上,两种情况可以统一处理:
- 计算 \(D = \sum \max(0, a_{i+1} - a_i)\)。
- 如果 \(D \ge M\),直接按上升沿构造,用 \(D\) 次操作。
- 如果 \(M > D\),从最大值位置切开环,按链处理,用 \(M\) 次操作。
连续树铲雪
这里我们需要一个插头的思想。
注意!此插头非彼插头(虽然有几位不愿意透露姓名的巨佬说它很简单)。
先假设每个点单独删,总代价为 \(\sum a_i\)。然后尽量让路径穿过多个点,用 \(f_i\) 统计最多能省掉多少次操作。
答案:
\\\text{ans} = \\sum_{i=1}\^{n} a_i - f_1 \\
我们设 \(g_u\) 为处理完 \(u\) 的子树后,还能向上延伸的插头条数。
对每个节点 \(u\),先计算所有儿子能提供的可用路径数:
\num = \\sum_{v \\in son(u)} \\min(a_u,\\; g_v) \\
累加儿子的节省值:
\f_u \\leftarrow \\sum_{v \\in son(u)} f_v \\
在 \(u\) 处进行路径合并:
\f_u \\leftarrow f_u + \\min(num,\\; 2a_u) \\
计算向上延伸的路径数:
\g_u = \\begin{cases} 0, \& num \> 2a_u \\\\ a_u, \& num \< a_u \\\\ 2a_u - num, \& a_u \\le num \\le 2a_u \\end{cases} \\
恭喜,我们做完了!
跳跃链铲雪 & 跳跃环铲雪 & 跳跃树铲雪
加上倍增即可。一道简化后的模板。
哎!别跑啊!难的还没来呢!
开个玩笑,那不是模板题。
跳跃链铲雪其实很简单,我们只需要新加一个数字 \(k\) 然后把它设为步长,最基础的链铲雪就是步长为 \(1\) 的情况,所以也就和普通的一样好做啦!
这里就不多复述了。
直接讲一下上面那道难题:
P6631
我们发现他有一个步长为 \(1\) 和步长为 \(2\) 的两种不同的操作(可惜了,去掉操作一就可以爆切黑题了),我们发现他极其不好维护,因为第一种操作会干扰第二种操作的最优策略。
我们考虑这样:
三种操作本质上都是在区间上放置若干条"线":
- 直线 :覆盖连续位置 \(i,i+1,i+2,\dots\);
- 跳线 :只覆盖奇偶性相同的位置,例如 \(i,i+2,i+4,\dots\)。
每个位置 \(i\) 必须被恰好覆盖 \(a_i\) 次。
目标是最小化使用的线数。
由于所有操作都是区间操作,我们可以从左到右依次决定每个位置如何被覆盖。
处理到位置 \(i\) 时,前面已经有一些线延伸到了 \(i\),它们可以免费继续覆盖 \(i\)。我们只需要决定:
- 还需要多少新线;
- 哪些线应该在 \(i\) 处结束;
- 哪些线可以继续往后延伸。
从左到右扫描时,维护:
| 变量 | 含义 |
|---|---|
z |
从前面延伸过来,能覆盖当前位置的直线数量 |
t0 |
从前面延伸过来,能覆盖当前位置的跳线数量 |
t1 |
从前面延伸过来,能覆盖下一个位置的跳线数量 |
其中 t0 和 t1 的区别只在于奇偶性。每处理完一个位置后,奇偶性会翻转,因此需要交换 t0 和 t1。
到达位置 \(i\) 时,前面已经能提供 \(z + t_0\) 次覆盖。
如果:
\z + t_0 \\le a_i \\
说明覆盖不够。此时直接把前面传过来的线全部用上:
\a_i \\leftarrow a_i - (z + t_0) \\
剩下的 \(a_i\) 必须用新线来覆盖。
反之如果:
\z + t_0 \> a_i \\
说明前面传过来的线太多了,有:
\k = z + t_0 - a_i \\
条线必须在位置 \(i\) 处结束。
但此时不能简单地优先砍直线或优先砍跳线,因为后面可能更需要其中某一种。
正确做法是反悔贪心:
-
计算 \(k = z + t_0 - a_i\)。
-
让 \(z\) 和 \(t_0\) 各减少 \(k\),但保证不为负。
-
被削减的 \(k\) 条线虽然结束了,但它们仍然可以看作从 \(i\) 出发的免费线。
-
这些免费线可以当作后续新线使用,因此把 \(a_i\) 加上 \(k\),同时把答案减去 \(k\)。
巨~~~ 爆切黑题!!!
处理完前面传过来的线后,当前位置还剩下 \(a_i\) 需要覆盖。
为了最小化操作数,应尽可能让一条线同时覆盖相邻两个位置。
因此先用直线同时覆盖 \(a_{i−1}\) 和 \(a_i\) 的重叠部分:
\add_z= \\min (a_{i−1},a_i) \\
这部分每条直线可以同时减少两个位置的值,所以最优。\(a_{i−1}\) 剩余的部分无法再和 \(a_i\) 重叠,只能用跳线单独处理。
处理完后,奇偶性翻转,因此交换 \(t_0\) 和 \(t_1\)。
:::infocode (请注意代码中有几处低级错误)
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
ll tc,n,a[N],z,jump_odd,jump_even,ans;
void solve(){
cin>>n,z=jump_odd=jump_even=ans=0;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=2;i<=n+1;i++){
int lxl=0;
if(z+jump_even>a[i]){
int k=z+jump_even-a[i];
if(k>z)
k-=k-z,jump_even-=k-z;
if(k>jump_even)
k-=k-jump_even,z-=k-jump_even;
z-=k,jump_even-=k,lxl=k,a[i]=0;
}
else
a[i]-=z+jump_even;
int x=min(a[i-1],a[i]);
z+=x,ans+=x,a[i-1]-=x,a[i]-=x,jump_odd+=a[i-1],ans+=a[i-1],a[i-1]=0;
a[i]+=lxl,ans-=lxl,swap(jump_even,jump_odd);
}
cout<<ans<<endl;
}
int main(){
cin>>tc;
while(tc--)
solve();
}
:::
真的可恶啊!!!
另外两种同理和普通环和普通树结合即可。
循环链铲雪 & 循环环铲雪 & 循环树铲雪
链结构
就是给定一个结构,你可以把其中一个特定形状的结构点权加 \(1\) 或者减 \(1\),每次搞完后的数对 \(k\) 取模,问要多少次操作才可以使得所有点权全变成 \(0\)。
这是他的普通形式。
让我们来考虑一下怎么实现。
正难则反不了一点!
让我们想一想前面的链铲雪是咋做的,先参与投票再到后面看答案:
投票:可以用什么? (点击选项进行投票):
:::info答案加解析
答案是差分。
为什么?
听我慢慢道来。
我们先设:
链上有 \(n\) 个点,权值为 \(a_1, a_2, \dots, a_n\)。
为了处理边界,我们引入虚拟节点 \(a_0 = 0\) 和 \(a_{n+1} = 0\)。
定义差分数组 \(\Delta\):
\\\Delta_i=a_i-a_{i-1}\\quad(1\\le i\\le n+1) \\
注意:
此时 \(\sum_{i=1}^{n+1}\Delta_i = a_{n+1} - a_0 = 0\)。
操作转化 :
对原数组区间 \(l, r\) 进行 \(+1\) 操作:
- \(a_l, \dots, a_r\) 均 \(+1\)。
- 差分变化:\(\Delta_l = a_l - a_{l-1}\) 增加 \(1\),\(\Delta_{r+1} = a_{r+1} - a_r\) 减少 \(1\)。
- 即:\(\Delta_l + 1,\Delta_{r+1} - 1\)。
同理,对区间 \(l, r\) 进行 \(-1\) 操作:
- 即:\(\Delta_l - 1,\Delta_{r+1} + 1\)。
结论 :
一次操作等价于在差分数组中选择两个位置 \(i, j\),一个 \(+1\),一个 \(-1\)。
我们的目标是让所有 \(\Delta_i \equiv 0 \pmod k\)。
由于每次操作总和不变(\(+1-1=0\)),且初始 \(\sum \Delta_i = 0\),我们只需要独立考虑每个 \(\Delta_i\) 需要多少次操作才能变成 \(0 \pmod k\)。
但在模 \(k\) 意义下,\(\Delta_i\) 可以变成 \(0, k, -k, 2k, \dots\)。
为了使操作次数最少,对于每个 \(\Delta_i\),我们实际上是要消除它的值。
令 \(\delta_i\) 为 \(\Delta_i\) 在模 \(k\) 意义下的最小非负剩余,即 \(\delta_i = \Delta_i \bmod k\)。
我们可以选择将 \(\Delta_i\) 变为 \(0\)(代价取决于正负平衡)或者利用全局平衡。
更简单的视角(贪心):
在普通整数域(不取模)的链铲雪中,最小操作次数是 \(\sum_{\Delta_i > 0} \Delta_i\)。
在模 \(k\) 意义下,每个 \(\Delta_i\) 可以视为 \(\Delta_i + m_i \cdot k\)。
我们需要选择整数 \(m_i\) 使得 \(\sum (\Delta_i + m_i \cdot k) = 0\) 且 \(\sum |\Delta_i + m_i \cdot k|\) 相关的某种度量最小?
不,对于 \(+1/-1\) 操作,最小操作次数等于:
\Ans = \\frac{1}{2} \\sum_{i=1}\^{n+1} \|\\pi_i\| \\
其中 \(\pi_i\) 是调整后的差分值,满足 \(\sum \pi_i = 0\) 且 \(\pi_i \equiv \Delta_i \pmod k\)。
为了使 \(\sum |\pi_i|\) 最小,我们通常取 \(\pi_i\) 为 \(\Delta_i\) 在 \((-\frac{k}{2},\frac{k}{2}]\) 范围内的代表元。
令 \(r_i = \Delta_i \bmod k\)。如果 \(r_i > \frac{k}{2}\),则取 \(\pi_i = r_i - k\),否则取 \(\pi_i = r_i\)。
此时 \(\sum \pi_i\) 可能不为 0,但如果原题保证有解,通常链的答案即为:
\\\text{Ans} = \\sum_{i=1}\^{n+1} \\max(0, \\pi_i) = \\frac{1}{2} \\sum_{i=1}\^{n+1} \|\\pi_i\| \\
否则就是无解。
注:这里假设操作可以任意配对。在链上,只要总差分和为 0,总是可以构造出对应的区间操作序列。
:::
接下来继续讲解环结构。
环结构
环上没有自然的首尾边界,\(a_{n+1} = a_1\)。
差分定义为 \(\Delta_i = a_i - a_{i-1}\) (\(1 \le i \le n\)),其中 \(a_0 = a_n\)。
此时 \(\sum_{i=1}^n \Delta_i = 0\) 恒成立。
难点:
在环上,操作区间 \(l, r\) 如果跨越了断点(例如 \(l > r\)),在差分上的表现不同。
或者更简单地:我们可以枚举环上的一个"断点"\(p\),假设在 \(p\) 处切开变成链。
但是,环上的操作允许我们给整个环所有点 \(+1\)。
这相当于没有改变任何差分,但在模 \(k\) 意义下改变了绝对数值?
不,题目要求全为 \(0\),所以绝对数值必须被消除。
关键性质:
在环上,给整个环 \(+1\) 的操作,对应差分全为 \(0\) 的变化。
这意味着差分系统无法捕捉全局偏移。
但是,我们要让 \(a_i\) 全为 \(0\)。
如果我们确定了差分数组 \(\Delta\) 的目标调整量,我们还需要确定一个全局基准。
解法:
-
计算初始差分 \(\Delta_i = a_i - a_{i-1}\)。
-
对于环,我们可以枚举其中一个点(比如点 \(1\))的最终累积调整量或者枚举断点。
-
更通用的方法:
环的问题可以转化为:
找到一个常数 \(C\),使得将链式差分中的某一项调整后,总成本最小。
具体来说,环上最小操作次数等于:
\\\min_{c \\in \[0, k-1} \left( \text{ChainCost}(\Delta_1+c, \Delta_2, \dots, \Delta_n-c) \right) \]
其中 \(c\) 代表我们在断点处引入的额外流量,\(ChainCost\) 表示把一条线性链上的所有点权,通过合法的区间 \(\pm 1\) 操作,全部归零所需的最小操作次数。
由于 \(+1/-1\) 的对称性,通常可以通过中位数定理或前缀和优化来快速求解这个最小值,复杂度 \(O(n \log n)\) 或 \(O(n)\)。
树结构
树上的操作通常针对子树 或路径。
情形 A:操作对象是子树
-
定义差分:\(\Delta_u = a_u - \sum_{v \in son(u)} a_v\)。
-
对子树 \(u\) 加 \(1\):\(\Delta_u + 1\),若 \(u\) 不是根,\(\Delta_{parent(u)} - 1\)。
-
这转化为了树上的点对点流量问题。
-
最小操作次数等于所有正差分之和(需满足守恒):
\\\text{Ans} = \\sum_{u, \\Delta_u \> 0} \\Delta_u \\quad (\\text{模 } k \\text{ 意义下需调整}) \\
同样,每个 \(\Delta_u\) 可以调整 \(k\) 的倍数。
由于树的结构限制,父子间的调整是耦合的。
通常使用树形 DP,自底向上确定每个节点向父节点传递的净流量,使得局部代价最小。
情形 B:操作对象是路径
- 使用树上差分:对路径 \(u-v\) 加 \(1\),等价于 \(\Delta_u+1, \Delta_v+1, \Delta_{lca}-2\)(具体视差分定义而定)。
- 这会将问题转化为在树的特定节点集上进行配对。
- 一般通过 DFS 序转化为线性问题,或利用树的重心分解等高级技巧。
给大家留一个思考,如果不只是加减 \(1\),那我们应该怎么做呢?
跳跃循环链铲雪 & 跳跃循环环铲雪 & 跳跃循环树铲雪
好做好做,我先假设你看懂了跳跃和循环,那你一定做这个很快。
我们直接把他分类,然后依旧按照循环铲雪的方法去做就好了。
基环树铲雪
什么你不知道啥是基环树???
之所以看上去简单实际上难,是因为环上的一条路径可以绕到任意位置,操作不再像树上那样天然分层,必须借助对偶思想把 "最大节省" 转化为一个可在环上线性 DP 的组合优化问题。
设:集族 \(\mathcal I\) 为所有合法操作(链或整环)。把原问题写成线性规划:
\\\min \\sum_{S\\in\\mathcal I} x_S \\quad\\text{s.t.}\\quad \\sum_{u\\in S} x_S \\ge a_u,\\; x_S\\ge 0 \\
取对偶并令 \(w_u\) 为点 \(u\) 的对偶变量(可取负值):
\\\max \\sum_u a_u w_u \\quad\\text{s.t.}\\quad \\forall S\\in\\mathcal I,\\ \\sum_{u\\in S} w_u \\le 1 \\
由 LP 强对偶定理,原问题最优值 \(=\) 对偶最优值。答案形式:
\\\mathrm{ans} \\;=\\; \\sum_u a_u \\;-\\; \\Phi,\\qquad \\Phi = \\text{对偶最大得分} \\
一条合法操作(链或整环)上的 \(w\) 之和不能超过 \(1\)。
状态设计
由于一条链的两个端点是 "独立" 的,对每个点 \(u\) 用四状态刻画它与父方向 / 邻方向的连接关系:
状态 \(0\):不与任何邻方向连通,\(w_u=-1\),此处为一条链的 "独立端点",贡献负权。
状态 \(1\):仅与父方向(或环上的一个邻)连通,\(w_u=0\),接收一个从下方 / 左方伸上来的插头。
状态 \(2\):从某个儿子(或环邻)发出插头,其余儿子断开,\(w_u=0\),向上发出一个插头。
状态 \(3\):与所有邻方向都连通(作为链的中间点),\(w_u=1\),两条插头在 \(u\) 处合并贯穿,贡献正权。
对偶权 \(w\) 之所以取 \(\{-1,0,+1\}\),来自约束 \(\sum_{u\in S}w_u\le 1\) 中链的两端必须 "凑够" \(1\) 的组合方式。
悬挂子树的树形 DP
对环上每个点 \(u\),把除环邻之外的子树独立处理。设 \(f_{0/1/2/3,u}\) 为该子树内的最大得分。转移:
\\\begin{aligned} f_{0,u} \&= -a_u + \\sum_{v\\in\\text{son}(u)} f_{0/1/2/3,v}\\\\ f_{1,u} \&= \\sum_{v\\in\\text{son}(u)} f_{0/1,v}\\\\ f_{2,u} \&= g(u) + \\sum_{v\\in\\text{son}(u)} f_{0/1,v}\\\\ f_{3,u} \&= +a_u + \\sum_{v\\in\\text{son}(u)} f_{0/1,v} \\end{aligned} \\
其中 \(g(u)\) 是 "从儿子中挑一个发出插头" 的最优增益:
\g(u)=\\max_{v\\in\\text{son}(u)}\\Bigl(\\max(f_{2,v},f_{3,v})-\\max(f_{0,v},f_{1,v})\\Bigr) \\
真复杂,前面还有一个巨佬想要搞仙人掌铲雪 qwq,我是真的搞不出来。
对了,再给点插头吧,真的很容易,你不开始怎么知道呢?
真不错,搞完吃薯片大赛后我也要搞一个。
单点清空铲雪
给定序列 \(a\),每次操作可将一个区间内的所有元素减 \(1\),或将某个点的值直接置 \(0\)。
操作过程中值不能为负,求使所有元素变为 \(0\) 的最小操作次数。
考虑笛卡尔树 DP。
一个重要观察是:
对于一个区间,要么直接推平(对区间内每个点各做一次单点置 \(0\),共 \(r-l+1\) 次),要么先整体减去区间最小值,再从最小值位置处分裂成左右两个子区间递归处理。因为减到一半再使用单点置 \(0\) 必然不如直接推平更优。
基于此,设 \(f_{l,r,v}\) 表示区间 \(l,r\) 已经整体被减去了 \(v\) 之后的最小操作次数。找到区间最小值位置 \(x\)(即 \(a_x=\min\limits_{i\inl,r} a_i\)),则:
\f_{l,r,v}=\\min(r-l+1,\\; f_{l,x-1,a_x}+a_x-v+f_{x+1,r,a_x}) \\
- 第一项 \(r-l+1\):直接对区间内每个点各做一次置 \(0\)。
- 第二项:花 \(a_x-v\) 次操作把整个区间从当前值 \(v\) 减到最小值 \(a_x\),然后左右子区间以新的已减量 \(a_x\) 递归求解。
把序列建成一棵小根笛卡尔树(以值为堆关键字、下标为 BST 关键字为啥我觉得有点像 Treap),每个节点对应一次找最小值并分裂。
DP 在树上自底向上做,每个节点只被访问一次,建树用单调栈 \(O(n)\) 完成,总复杂度 \(O(n)\)。
这想法真的很实用,参考至 CSDN。
树上双叶铲雪
就是树上面选取的链有了限制,两个端点都要是树的叶子。
模板题。
这玩意儿比树上铲雪容易多了,我应该先拿他当前置的,没关系先提提神后面有终极 BOSS。
依旧线头法(何其不是插头呢?):
一次操作 \((u,v)\),其中 \(u,v\) 都是叶子,相当于在树上选了一条 \(u\to v\) 的路径,路径上每个点的覆盖数 \(+1\)。最终要使每个点的覆盖数恰好等于 \(a_u\)。
与普通铲雪(端点可以取到任何点)相比,本题的关键限制是:链的两端必须是叶子。因此对于任意一个非叶子节点 \(u\),从儿子延伸上来的路径只有两种归宿:
- 与另一条来自不同儿子的路径配对,穿过 \(u\) 后落到 \(u\) 下方的另一片叶子(此时 \(u\) 被覆盖 \(1\) 次);
- 继续向上延伸到 \(u\) 的父亲(此时 \(u\) 被覆盖 \(1\) 次)。
不存在在 \(u\) 结束,从 \(u\) 出发这种链,\(u\) 不是叶子,做不了端点。
定义与核心方程
任取一个度数 \(>1\) 的节点作根(\(n=2\) 时两个点都是叶子,需特判)。令:
- \(\delta_u\):点 \(u\) 向父亲方向延伸的路径条数;
- \(f_u=\sum_{v\in \text{son}(u)}\delta_v\):从所有儿子延伸上来的路径总条数。
叶子 :覆盖它的链必以它为端点向上延伸,故 \(a_u=\delta_u .\)
非叶子 :\(f_u\) 条链在 \(u\) 处,设其中 \(2k\) 条两两配对穿过 \(u\)(贡献 \(k\)),剩下的 \(\delta_u\) 条继续向上(贡献 \(\delta_u\)),于是:
\a_u = k+\\delta_u = \\frac{f_u-\\delta_u}{2}+\\delta_u = \\frac{f_u+\\delta_u}{2} \\
整理得
\\\boxed{\\ \\delta_u = 2a_u - f_u\\ } \\
也就是说,每个点的 \(\delta_u\) 由子树唯一确定 ,没有任何自由度可选,这正是它比插头好写的原因(顺便呼应一句:何尝不是插头呢)。
可行性判定
由 \(\delta_u = 2a_u - f_u\),只需自底向上递推并在每个非叶子点做如下检查:
非根非叶点 \(u\):
- \(\delta_u \ge 0\),即 \(f_u \le 2a_u\);
- \(\delta_u \le a_u\),即 \(a_u \le f_u\)(保证配对数 \(k=f_u-a_u\ge 0\));
- \(\max_{v\in\text{son}(u)}\delta_v \le a_u\)。
第三条来源:配对只能发生在两个不同儿子 的链之间,故可行配对数至多为 \(f_u-mx\),需要 \(k=f_u-a_u \le f_u-mx\),即 \(mx\le a_u\)。这是本题最经典的坑点。
根 \(r\): 不能再向上延伸,需满足 \(f_r = 2a_r,\qquad \max_{v\in\text{son}(r)}\delta_v \le a_r .\)
叶子:\(\delta_u=a_u\),天然合法。
\(n=2\): 两点都是叶子,判 \(a_1=a_2\)。
无需单独判奇偶:\(\delta_u=2a_u-f_u\) 自动保证 \(f_u+\delta_u=2a_u\) 为偶数。
根的选取:\(n\ge 3\) 的树必有度数 \(\ge 2\) 的点。换一个非叶根只是换了一种分解方向,按边统计的 \(\delta\) 不变,判定结果一致。
为什么必须判 \(\max\delta_v\le a_u\)
取根 \(r\) 的两个儿子链数为 \(\delta=3,1\),\(a_r=2\)。此时 \(f_r=4=2a_r\) 看起来全对,但那 \(3\) 条同源链最多配出去 \(1\) 对,剩下 \(2\) 条无处可去,判定应为 No。这正是 \(\max\delta_v>a_u\) 的情况。
好了水题讲完了,准备好接受 BOSS 的审视了吗?
二元铲雪
给大家留一个思考,如果不只是加减 \(1\),那我们应该怎么做呢?
还记得上面的那个问题吗?
这就是二元铲雪,真的要吐,恶心至极!!!
模板题。
没错!就是模板,不是那个跳跃铲雪的思维题!!!
这可是唯一一道涉及到数学的铲雪题:
裴蜀定理判无解
我们知道每个位置最终值一定形如 \(a x_i + b y_i\)(\(x_i,y_i\in\mathbb Z\))。由裴蜀定理,所有 \(h_i\) 能被表示当且仅当 \(\gcd(a,b) \mid h_i\) 对所有 \(i\) 成立;若不成立直接输出 \(-1\)。
有解时,把 \(a,b,h_i\) 同时除以 \(\gcd(a,b)\),问题形式不变,故下文不妨设 \(\gcd(a,b)=1\)。
差分转化
经典结论:对区间 \(l,r\) 的 \(\pm a\) 操作等价于差分 \(\Delta_l \gets \Delta_l\pm a,\ \Delta_{r+1}\gets \Delta_{r+1}\mp a\)。
我们令:
\\\Delta_i = h_i - h_{i-1}\\quad(1\\le i\\le n),\\qquad \\Delta_{n+1} = -h_n \\
则目标变为 \(\forall i,\Delta_i=0\),且保证 \(\sum \Delta_i = 0\)。下文令 \(n\gets n+1\)。
对每个差分值 \(\Delta_i\),设进行了 \(x_i\) 次 \(+a\)、\(y_i\) 次 \(+b\)(负数表示区间减),即:
\a x_i + b y_i = -\\Delta_i \\
由前面的结论,全局操作次数为:
\\\frac{1}{2}\\sum_{i=1}\^n \\bigl(\|x_i\|+\|y_i\|\\bigr). \\
对每个 \(\Delta_i\) 独立求最小代价
先用 exGCD 求出 \(a x_0 + b y_0 = 1\) 的一组特解,则 \(a x_i + b y_i = -\Delta_i\) 的通解为
\x_i = x_0' + k\\cdot b,\\qquad y_i = y_0' - k\\cdot a \\
(\(x_0' = -x_0 \Delta_i\),\(y_0' = -y_0 \Delta_i\),\(k\in\mathbb Z\))。
\(|x_i|+|y_i|\) 关于 \(k\) 是一个分段线性凸函数 :斜率只在 \(x_0'+kb=0\) 或 \(y_0'-ka=0\) 两个拐点处发生改变,因此最优解 \(k^\star\) 一定落在拐点附近。对每个 \(i\) 直接枚举这两个拐点及相邻一两个整点,取 \(|x_i|+|y_i|\) 最小者即可,\(O(1)\) 完成单点最小化。
记此时的解为 \((x_i^\star, y_i^\star)\),代价 \(c_i^\star = |x_i^\star|+|y_i^\star|\)。
全局约束 \(\sum x_i = 0\) 与调整
到目前为止得到的 \(\frac{1}{2}\sum c_i^\star\) 只是下界,不一定可行 ------ 每一次区间操作都会同时触碰到一个左端点和一个右端点,因此必须满足
\\\sum_{i=1}\^n x_i = 0,\\qquad \\text{(等价于)}\\ \\sum_{i=1}\^n y_i = 0 \\
由 \(a\sum x_i + b\sum y_i = -\sum \Delta_i = 0\) 且 \(\gcd(a,b)=1\),两个条件等价,只需考虑 \(\sum x_i\)。
由通解形式,把某个 \(x_i\) 加上 \(b\) 同时把对应 \(y_i\) 减去 \(a\),方程 \(a x_i + b y_i = -\Delta_i\) 仍然成立 ------ 称对 \(i\) 做了一次调整,本次调整的代价为
\\\Delta_i = \\bigl(\|x_i+b\|+\|y_i-a\|\\bigr) - \\bigl(\|x_i\|+\|y_i\|\\bigr). \\
设 \(X = \sum x_i\):
- 若 \(X < 0\):需要 \(-X/b\) 次 \(x_i \gets x_i + b\) 调整;
- 若 \(X > 0\):需要 \(X/b\) 次 \(x_i \gets x_i - b\) 调整。
\(X/b\) 一定是整数:\(aX + b\sum y_i = 0\) 推出 \(b\mid X\)。
代价的凸性 :\(\Delta_i\) 随调整次数增加是凸的,且仅在 \(x_i\) 或 \(y_i\) 跨过 \(0\) 时改变斜率,其它时候不变。这正是用堆维护的依据。
反悔贪心 :用小根堆维护每个 \(i\) 当前的单位调整代价,每次取出代价最小者,在需求次数与不跨过下一个拐点两个限制下尽可能多地一次调整到位,把新拐点处的 \((\text{代价},\ i)\) 塞回堆。由于 \(x_i,y_i\) 各自最多跨过一次 \(0\),整个过程最多发生 \(O(n)\) 次有效拐点事件,总复杂度 \(O(n\log n)\)。
拐点分段的表示
由于 \(\Delta_i\) 分段线性且斜率只有 \(O(1)\) 段,可以预先对每个 \(i\) 枚举所有拐点,得到形如 \((w_j, s_j)\) 的列表:表示以单位代价 \(w_j\) 连续移动 \(s_j\) 步 。把所有 \((w_j,s_j)\) 按代价 \(w_j\) 升序排序,需要 \(T = |X|/b\) 次调整时按顺序取即可 ------ 这就是神奇调整法。
按参考实现,只需在每次弹出堆顶后最多再塞回一次即可,正确性由下面这条性质保证。
为何每轮只需一次调整?
结论:\(|X|/b = O(n)\),且从堆中取出一个 \(i\) 后做一次调整就塞回,也是正确的。
证明(来自题解评论区):
设 \((x_i, y_i)\) 已取到 \(|x_i|+|y_i|\) 最小。
若 \(x_i\) 已是最小非负整解或最大负整解,则 \(|x_i| \le b\),于是 \(|X| \le \sum |x_i| \le n b\)。
若 \(y_i\) 已取到极值解,则 \(|y_i|\le a\),代入
\\\left\|\\frac{-Xa + \\sum d_i}{b}\\right\| \\le n a \\
并利用 \(\sum d_i = 0\),同样得到 \(|X| \le n b\)。因此 \(|X|/b \le n = O(n)\),堆中每轮只产生一次有效弹出,无需对每段重复弹出。
关于非负性限制
题目要求操作过程中点权始终非负。
本题的标准处理是:
先忽略该限制求出一组最优操作多重集,再论证这些操作总能重排成一条合法的执行序列。
直观原因是:
任一时刻所有点权之和为 \(0\)(差分总和不变),且每一步操作都是一段区间内所有点同时 \(\pm c\)。
可以按当前最小正值 \(\gets\) 用一个能覆盖它的减操作的方式贪心归约,类似辗转相除地消耗,每一步都不会让任何点降到负。
具体地,如果某个减操作会让某点变负,就先在该点所在位置执行对应的加操作抵消,归约过程与欧几里得算法同构必然终止。
因此代数上得到的最优操作数即为答案;若裴蜀判定失败,直接输出 \(-1\)。
后记
:::infoAI 润色
本文未使用 AI。
使用格式化工具格式化。
:::
:::info参考资料
:::