1. 题目里那句“在你窗外”究竟翻译成什么数据结构
每年备考 GESP C++ 五级的学生,几乎都会被群里丢过来的 luogu-P3353 “在你窗外闪耀的星星”折磨一下。这道题代码量很短,短到很多同学以为它是签到题;可它真正考的是能不能把“窗外有多少颗星星”准确翻译成一段连续区间求和,翻译错一个下标,就是接二连三的 WA。准备考 GESP 五级的同学,如果你正在学前缀和、数组下标处理、枚举优化,那这道题就是一块非常标准的试金石。
先把最常见版本的题意剥给你看:天空被简化成一条一维坐标轴,星星是一个个点,每颗星星有两个属性,坐标 x 和亮度 b。你可以移动一扇窗户,窗户的宽度固定为 w。问窗户放在什么位置时,能看见的星星亮度之和最大。同一个坐标上可能同时有多颗星星,它们会全部被看见,亮度按加法叠加。
我上课的时候喜欢让学生先别写代码,而是把这个场景翻译成数据结构语言。一颗星星用一个点表示,那整条坐标轴其实就是一个桶数组 star[],下标是坐标位置,值是这个位置上所有星星的亮度总和。窗户是一段固定长度的连续区间,移动窗户就是枚举这段区间的左端点。题目答案就是:在所有可能的窗口区间里,找一个区间和最大的值。
这道题适合所有正在备考 GESP 五级,或者刚开始准备入门信息学竞赛的 C++ 学习者。因为它的难点不在语法,而在“模型转换”和“边界处理”这两个非常容易被忽略的环节。你哪怕只会数组和循环,也能读懂暴力的写法;但如果你想拿满分,就必须把复杂度从两层循环优化到一层循环。下面我把整个分析过程完整走一遍,包括我平时带学生时反复强调的易错点。
1.1 去掉包装后的数学模型
如果你只在洛谷上读过题面,很容易被“窗外”“星星”“闪耀”这些词带偏,觉得这是一道模拟题。其实它和天文、坐标轴没有任何关系,核心模型非常干净:
给定一个长度为 N 的一维数组 star[],star[i] 表示位置 i 上的亮度总和。给定整数 w,表示窗户可以覆盖一段连续区间。设窗户左端放在 l,右端放在 r,则满足 r - l = w(这是“宽度差”的常见定义)。请你求:
max( star[l] + star[l+1] + ... + star[r] )
看到这个式子,你就应该有一种直觉:这是典型的“连续区间求和”问题。连续区间求和有几个常用的工具,最基本的就是前缀和
