题目大意
题目描述总结
给定一个长度为 的数列 和一个定值 。两名玩家(铃仙和因幡帝)交替从数列中取数,取出的数会立刻从原数列中删除。规则如下:
-
铃仙先手,每次可以从当前数列中任选一个数。
-
因幡帝后手,每次必须选择当前数列头部的前 个数(即剩下数列的第 到第 个数)。若当前数列剩余元素不足 个,则因幡帝会将剩余的所有数全部取走。
-
两人交替选数,直到数列为空。
铃仙的目标是最大化自己所取出的所有数字之和。
假设铃仙始终采取最优策略,请对于所有的 ,分别独立求出铃仙所能获得的数字和的最大值。
输入格式
第一行包含一个整数 (),表示测试用例数量。
每个测试用例包含两行:
第一行是一个整数 (),表示数列长度。
第二行是 个正整数 (),表示给定的数列。
保证所有测试用例中 的总和不超过 。
输出格式
对于每个测试用例输出一行,包含 个正整数。第 个整数表示当 时,铃仙能获得的最大数字和。
样例数据
输入样例:
1 | 3 |
输出样例:
1 | 2 |
样例解释
在样例 中,初始数列为 。
当 时,一种使得铃仙获得最大和的方案为:
-
铃仙先选取 ,删除该数,此时数列变为 ;
-
因幡帝必须选取头部的 个元素 ,此时数列变为 ;
-
铃仙再选取 ,此时数列变为 ;
-
因幡帝必须选取头部的 个元素 ,此时数列变为 ;
-
铃仙再选取 ,此时数列变为 ;
-
因幡帝必须选取头部的 个元素 ,此时数列为空,停止。
铃仙所选之数的和为 。可以证明,该方案铃仙所选之数的和最大。
当 时,一种使得铃仙获得最大和的方案为:
-
铃仙先选取 ,删除该数,此时数列变为 ;
-
因幡帝必须选取头部的 个元素 ,此时数列变为 ;
-
铃仙再选取 ,此时数列变为 ;
-
因幡帝必须选取头部的 个元素,但当前数列大小小于 ,全部取完,此时数列为空,停止。
铃仙所选之数的和为 。可以证明,该方案铃仙所选之数的和最大。
思路讲解
说实话,看完这道题目,我的感觉是不是特别困难。
那么,这道题目的第一个难点就是时间复杂度分析:
呃,其实我们会发现,使用数据结构优化的这个贪心,因为有调和级数的保障,时间复杂度是对的。
或者,换句话说,如果我们的这个**每次铃仙选数的次数(在所有 x 下)**加起来是 ,是不是就是这个调和级数?
然后这道题目的第二个难点就是这个贪心策略地选择:
⚠️注意:容易想到的贪心策略有~~不断地取前 x+1 个中最大的数字,不断地取全局最大数字~~,但是这些都是错的!
我完全懂你的意思!你提出的这个贪心策略非常直观且符合直觉:既然一轮游戏刚好消耗 个数,那我就每次盯着最前面的 个数,铃仙拿走里面最大的,剩下的 个恰好留给因幡帝按规则从头吃掉,完美闭环!
但在仔细推敲后,这个策略其实是有漏洞的,它极大地限制了铃仙的发挥。
我们可以用一个非常简单的反例来验证:
假设 ,现在的数组是:10, 20, 100, 90。
按照你的“分块模拟”贪心策略:
- 第一轮:看最前面的 个数,也就是
10, 20。铃仙拿走最大的20,剩下的10被因幡帝吃掉。 - 第二轮:看剩下的
100, 90。铃仙拿走最大的100,剩下的90被因幡帝吃掉。 - 铃仙最终得分:。
但如果铃仙采取真正的“全局最优策略”呢? - 第一轮:铃仙发现前面的数全都是垃圾,她根本不看前面,直接从全局选了那个最大的
100!- 拿走
100后,数组变成了10, 20, 90。 - 轮到因幡帝,因幡帝是个死脑筋,规则限制她必须吃掉最头部的 1 个数,于是她悲惨地吃掉了垃圾数字
10。 - 一轮结束后,剩下的数组是
20, 90。
- 拿走
- 第二轮:铃仙再拿走最大的
90。- 因幡帝吃掉最后的
20。
- 因幡帝吃掉最后的
- 铃仙最终得分:!
你看! 远远大于你的策略得出的 。
假设 ,现在的数组是:99, 10, 100, 10。
如果你采用“每次拿全局最大”的策略:
- 第一轮:全局最大是
100,铃仙不管三七二十一,先抢走100。- 拿走后,数组变成
99, 10, 10。 - 轮到因幡帝,因幡帝吃掉头部的 1 个数,惨剧发生了——因幡帝把
99吃掉了!
- 拿走后,数组变成
- 第二轮:数组只剩
10, 10。铃仙只能含泪拿走一个10。 - 铃仙最终得分:。
但如果铃仙是个战术大师呢? - 第一轮:铃仙纵观全局,发现最大的
100排在第三个位置,它非常“安全”,因为因幡帝这轮只能吃掉第 1 个数,根本碰不到它!但排在第一位的99极度危险,如果现在不拿,下一秒就会被吃掉。- 所以铃仙第一轮强忍住拿
100的诱惑,优先保下了99****! - 拿走
99后,数组变成10, 100, 10。 - 轮到因幡帝,因幡帝吃掉头部的 1 个数,也就是垃圾数字
10。
- 所以铃仙第一轮强忍住拿
- 第二轮:数组剩下
100, 10。那个原本安全的100现在暴漏出来了,铃仙笑纳100。 - 铃仙最终得分:!
你看! 再次碾压了你 的策略。
我们可以采用这道题目的一个思路,就是,截止时间,ddl 非常难以处理,但是反过来看,就是这个激活时间,激活时间以后,这个点就想选就选,不想选就不选了。
然后,我们再来看这个东西,就是我们已经想到了这个 ddl 就是这个激活时间,然后我们现在的问题就是每个点的这个 ddl 是什么,能不能批量处理呢?
我们来看最差情况下和最好情况下的这个差别:最坏情况下(也就是假设所有的我们自己选的都在前面),第 次操作的剩余数字的下标要求是 。在最好情况下,第 次操作的剩余数字的下标要求是 。那么 中的这个数字的 ddl 是 ,即在 次操作之前,这个数都是一定可以选的。。
通过上面👆的这个分析,我们会发现 ddl 如果采用真实游戏结束回合的定义,也就是什么时候会被取走,那么这个点的 ddl 就不是固定的,这非常不适合我们进行优化以及操作。
所以 ddl 应该采用这个所谓的前缀容量限制的定义方法。严格来说,对于 ,其指的是以 为前缀的这个序列,在游戏中,可以最多保住多少个数字?
一个很小的例子就能看出这两个概念不同。比如 ,看位置 :
- 它在原游戏里是可能活到第 次轮到你之前的,因为你第一轮如果先拿后面的数,帝会删掉前 个, 还在。
- 但它的静态 deadline 是
- 这表示的是:在前缀 里,最终最多只能保住 个数,所以如果你想保住位置 ,它只能是你选中的所有点里“最小下标的那个”,不能再同时保住前面别的点。
所以你的那段分析没有问题,问题只是在于“ddl到底指哪一种量”。如果指真实轮次,就不固定;如果指转化后的前缀容量 deadline,它才是固定的。
第十一届中国大学生程序设计竞赛网络预选赛(CCPC Online 2025)——造桥与砍树
这个其实用了这个类似于懒操作的这个设计思想,不过其实也不是很一样。以上面👆的这个例子为例,虽然 可能存活到第 2 回合,但是其存不存活到第二回合都不重要,因为即便其存活到第二回合,还是改变不了 这个前缀最多只能保住一个数字的这个事实。所以说,可以等效认为其只能存活到这个第一回合。
那么 这个前缀可以保住多少个这个数字呢?其实就是 。那反过来讲,其实就是 的 dll 都为为 1,因为最多只能保下来 1 个这个数。 的 ddl 就是 2,以此类推x
AC代码
AC
https://acm.hdu.edu.cn/contest/view-code?cid=1198&rid=19436
1 | // teamname: Gospel_rock |
心路历程(WA,TLE,MLE……)
注意,我们查的都是后缀。

不要因为历史原因,有的改了,有的没改,导致不一致。

不要忘记这里的初始化。
