第一次做这道题的时候,我盯着“校验值”的定义看了二十分钟没动手。不是因为题目有多长,而是这个定义绕得厉害:又要选数对,又要最大化平方和,还要分段数最少。更难受的是,题目写着“ACM”,明摆着提示你二分和倍增,可真要往代码上落地,边界条件一个比一个阴。这道题就是 AcWing 109 / CH 0601 上的“天才ACM”,一道非常典型的“二分答案思路 + 倍增扩展实现”的题。今天我把完整思路、证明、两种写法和踩坑点都拆开讲一遍。
如果你刷题卡在这道题上,或者刚学完二分、倍增想找个综合题练手,这篇应该能帮你从“看着题解写代码”变成“自己会推结论”。
1. 题目到底在问什么:校验值、分段与最小段数
1.1 题意拆解:校验值不是“所有数的和”,而是“最大平方差和”
题目给了一个长度为 N 的整数数列 A,还给了一个正整数 M 和一个大整数 T。对任意一个区间/集合 S,定义一种叫“校验值”的东西,计算规则是:
从 S 中取出若干对互不重复的数,每对数的差的平方相加,要求这个总和尽可能大。最多能取 M 对,但如果 S 里的数不够 2M 个,就尽量多取。这个“最大总和”就是 S 的校验值。
举个例子。假设区间是 [5, 1, 2, 6],M = 2。
先排序得到 [1, 2, 5, 6]。最多取 2 对数,也就是 4 个数,区间正好 4 个数。怎么配?
- (1,6) 和 (2,5):差的平方和 = (6-1)² + (5-2)² = 25 + 9 = 34
- (1,5) 和 (2,6):平方和 = (5-1)² + (6-2)² = 16 + 16 = 32
- (1,6) 和 (2,5) 明显更大。
所以校验值就是 34。
而题目要做的事情是:把整个数列 A 切成若干连续段,每一段的校验值都不能超过 T,求最少能切成多少段。
这是一个典型的最优化问题。数据范围通常 N 能到 5e5 甚至更大,T 和平方和动不动超过 int,所以时间复杂度和 long long 问题都得认真对待。
1.2 一个关键的数学结论:排序后首尾配对就是最优配对
很多初学者卡在第一步:校验值到底怎么算最快?枚举所有配对方案肯定不可能,因为组合数爆炸。但有一个重要结论:把区间内所有数排序后,取最小的数和最大的数配对、次小的数和次大的数配对,这样得到的平方和最大。
证明思路不复杂,用交换论证。
假设当前有一对 (a, d) 和 (b, c),并且 a ≤ b ≤ c ≤ d。如果配对方式是 (a, c) 和 (b, d),那么校验值是 (c-a)² + (d-b)²。如果改成 (a, d) 和 (b, c),校验值是 (d-a)² + (c-b)²。作差:
(d-a)² + (c-b)² - [(c-a)² + (d-b)²]
展开化简得到 2(d-c)(b-a) ≥ 0。也就是说,把较大的数和较小的数配在一起,值只会更大或不变。所以最后的极值一定出现在“数值最小的那一端”和“数值最大的那一端”之间配对。
再进一步:如果区间长度超过 2M,也就是可选的数比需要的多,那么中间那部分“不上不下”的数根本不会被选到。因为我们要取 M 对,只关心最小的 M 个数和最大的 M 个数。中间的数既不是足够小,也不是足够大,参与配对反而是浪费。
这个结论直接决定代码怎么写:排序后,取前一半的较小值和后一半的较大值,从两端向中间配对,只算前面那段平方差和就行。
1.3 贪心拆段的正确性:合法区间是单调的
接下来是“最少分段”这一步。很多人习惯性地想 DP,但 n 到 5e5 直接劝退。这里其实可以直接贪心:每次从当前位置开始,取一段最长的合法区间作为一个段,然后从这段后面继续,段数就是最少的。
贪心为什么是正确的?因为校验值具有单调性:一个区间合法,那么它的任意子区间也一定合法。因为子区间的元素更少,可配对的组合只会变少,而且平方差和也不可能变大。
用反证法描述:假设最优解第一段是 [1, r'],而我们贪心选的第一段是最长合法前缀 [1, R],且 R > r'。由于 [1, R] 合法,我们把第一段扩展到 R,剩余部分从 R+1 开始继续分。原本从 r'+1 开始的分段方案依然可以套用到从 R+1 开始的部分,段数不可能变多。所以贪心每次取最长合法段,必然得到最小段数。
于是问题被简化成了:给定起点 l,每次找最远的 r,使得区间 [l, r] 的校验值 ≤ T。只要能高效完成这个“找
