注意 dfs 要记得进行回退操作的时候,所有东西都要回退,包括这个 vis 数组。

注意 dfs 要记得进行回退操作的时候,所有东西都要回退,包括这个 vis 数组。

当一个东西需要不断反复地去加上一个乘积的时候,你要当心,是不是有可能爆 longlong。

https://acm.hdu.edu.cn/contest/clarification?cid=1199&tid=24362
因为爆 longlong 而 WA https://acm.hdu.edu.cn/contest/view-code?cid=1199&rid=1950

题目描述
有一个大小为 3×3 的矩形安全箱。
共有 n 件物品,第 i 件物品的大小为 ai×bi(可以水平放置或旋转 90∘),价值为 vi。
你需要选择若干件物品放入安全箱内,物品必须完全在箱子内部且互相之间不能有重叠部分。
求能够放入安全箱内物品的最大总价值。
输入格式
第一行输入一个整数 T(1≤T≤10000),表示测试数据的组数。
对于每组数据:
第一行输入一个正整数 n(1≤n≤106),表示物品的数量。
接下来 n 行,每行输入三个整数 ai,bi,vi(1≤ai,bi≤3,1≤vi≤108),分别代表第 i 件物品的长、宽和价值。
保证所有测试组的 n 之和不超过 106。
输出格式
对于每组数据,输出一行一个整数,表示最大的总价值。
样例输入
1 | 2 |
样例输出
1 | 28 |
样例解释第一组数据:最优策略是放入第 1 件物品(大小 2×3,价值 20)、第 3 件物品(大小 1×2,价值 4)以及第 4 件物品(大小 1×1,价值 4)。第 1 件物品占用 2×3 的空间后,安全箱还会剩余 1×3 的空白区域,恰好可以无重叠地容纳第 3 件和第 4 件物品。总价值为 20+4+4=28。
第二组数据:虽然第 2 件物品(大小 3×3,价值 6947311)能正好填满整个安全箱,但只放入第 1 件物品(大小 1×1,价值 13141314)可以获得更高的总价值。因此最优选择是仅放置第 1 件物品,最大总价值为 13141314。
不难发现,一共只有 21 种有效状态。
开个玩笑,这个是提前用 dfs 跑出来的,直接打表,跳过这个预处理。

那你都预处理出来所有的这个情况了,做起来就很简单了。
AC
https://acm.hdu.edu.cn/contest/view-code?cid=1199&rid=13515
1 | // teamname: Gospel_rock |
常见 dfs 错误及原因

题目背景与目标
有 n 张牌从上至下组成的牌堆,每张牌上有一个点数 ai。共有 m 名玩家按 1,2,…,m 的顺序依次循环摸牌。玩家 k 作为游戏组织者,在所有人开始摸牌之前,可以进行至多一次切牌操作(即将牌堆从任意位置分为上下两部分,并交换这两部分的位置)。目标是求出玩家 k 通过合理切牌,最终自己摸到的牌的点数总和的最大值。
输入格式
第一行包含一个整数 T,表示测试用例的数量。
每组数据包含两行:
第一行输入三个正整数 n,m,k,分别代表牌的总数、玩家的总数以及目标玩家的顺位。
第二行输入 n 个整数 a1,a2,…,an,自顶向下给出牌堆中每张牌的点数。
输出格式
对于每组数据,输出一行一个整数,表示玩家 k 可以获得的最大点数和。
样例输入
1 | 2 |
样例输出
1 | 1919810 |
样例解释第一组样例:牌堆共 2 张牌,有 2 名玩家,主角是第 1 名玩家。初始牌堆点数为 114514, 1919810。主角可以通过切牌操作,将上面的第一张牌移到最下方,得到新牌堆 1919810, 114514。开始发牌后,主角作为第 1 名玩家摸走第一张牌,获得最大点数 1919810。
第二组样例:牌堆共 3 张牌,只有 1 名玩家,主角是第 1 名玩家。无论主角在开始前如何切牌,因为只有他一个人玩,他都会摸走所有的牌。因此他获得的总点数固定为 1−3+2=0。
给定一个包含 n 张牌的牌堆,每张牌有点数 ai。有 m 个玩家依次循环摸牌(即玩家 1 摸第 1 张,玩家 2 摸第 2 张,……,玩家 m 摸第 m 张,然后玩家 1 再摸第 m+1 张,以此类推),直到牌摸完。
玩家 k 可以在摸牌前进行最多一次操作:将牌堆从任意位置切开,交换上下两部分。这等价于对数组进行任意次数的循环移位。
求玩家 k 能摸到的牌的点数总和的最大值。
首先分析玩家 k 摸到的牌的数量和位置。
由于牌是按照 1 到 m 循环分发的,在洗牌(循环移位)后的新牌堆中,玩家 k 必定摸走下标为 k,k+m,k+2m,… 的牌(下标从 1 开始)。
因为牌堆总数为 n,所以玩家 k 摸到的牌的数量始终为定值 C=⌊mn−k⌋+1。
无论我们将原牌堆循环移位多少次,新牌堆中下标相邻为 m 的位置,在原牌堆中它们的下标差值也必定是 m(modn)。
因此,玩家 k 摸到的 C 张牌,在原数组中恰好对应了一条从某个起点开始、每次下标加上 m(modn)、长度为 C 的路径。
这提示我们将原数组的下标视为一个图,每个下标 i 向 (i+m)modn 连边。
根据数论性质,这个图由 g=gcd(n,m) 个不相交的环构成,每个环的长度为 L=gn。
由于循环移位的偏移量 s 可以取 0 到 n−1 中的任意整数,路径的起点可以取遍所有 n 个位置。
所以,问题转化为:在这 g 个环中,寻找所有长度恰好为 C 的连续子段,求出其中点数总和的最大值。
算法步骤如下:
1 | #include <iostream> |
vector 所占空间均为 O(n),空间复杂度为 O(n),完全符合 256 MB 的限制。ans = -2e18 防止了将负答案误判为 0 的漏洞。其实这道题目的思路说起来也很简单,就是在一个环上跑这个滑动窗口。
这个切牌其实可以看作循环位移操作。

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

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

不要忘记这里的初始化。
