CSP-J 2026 纸牌游戏 题解
Error_Yuan
·
2026-11-02 19:22:32
·
题解
好题啊!!
题目大意
给定一叠共 n 张的牌,从上到下的点数依次为 a_1,a_2,\ldots,a_n 。共 m 种点数,每种点数 1,2,\ldots,m 都至少出现一次。每局游戏开始时桌面为空,打出一张点数为 k 的牌时:
如果桌面上没有点数为 k 的牌,就将这张牌放在最右端;
否则,先收走桌面上点数为 k 的牌及其右侧的所有牌,再将新打出的牌放在最右端。
现在进行 n 局相互独立的游戏,第 i 局依次打出第 i 张至第 n 张牌。对于每种点数,求有多少局游戏结束时,桌面上留有该点数的牌。
数据范围:2\le n \le 5\cdot 10^5 ,1\le m\le 3\cdot 10^5 。
算法一
根据题意暴力模拟,每局游戏中每次打出一张牌,都使用 \mathcal{O}(n) 的方法遍历并修改桌面上的牌,总时间复杂度 \mathcal{O}(n^3) ,可以通过测试点 1\sim 6 ,期望得分 24 。
算法二
同算法一暴力模拟,但每局游戏时使用栈维护桌面上从左到右的点数序列,并用数组记录每种点数是否在栈中。每张打出的牌至多入栈和出栈各一次,因此一局游戏模拟所需复杂度为 \mathcal{O}(n) ,总时间复杂度 \mathcal{O}(n^2) ,可以通过测试点 1\sim 12 ,期望得分 48 。
算法三
当序列 a 单调不减时,相同点数的牌连续出现,只会替换桌面上最右端的同点数牌,不会收走其他点数的牌。因此,第 i 局游戏结束时桌面上的点数序列,即序列 a_i,a_{i+1},\ldots,a_n 去重后的结果。
设 \mathrm{last}_x 为点数 x 最后一次在序列中出现的位置,则第 i 局游戏结束时留有点数 x 的牌,当且仅当 i\le \mathrm{last}_x ,因此点数 x 对应的答案即为 \mathrm{last}_x 。
此算法可以通过所有特殊性质 A 测试点,即测试点 7\sim 9 和 20 ,期望得分 16 ,结合算法二可以获得 52 分。
算法四
当 m=2 时,设最后一张牌的点数为 x ,另一种点数为 y 。点数 x 的牌会在所有局游戏结束时留在桌面上,其答案为 n 。
如果第一张打出的牌的点数为 y ,那么桌面最左侧始终留有点数为 y 的牌,最后桌面上的点数序列为 [y,x] ;否则,最左侧的点数始终为 x ,打出最后一张牌后,桌面上只剩点数为 x 的牌。因此,点数 y 对应的答案就是它在序列中的出现次数,直接统计即可。
此算法可以通过测试点 1\sim 3 与 13, 14 ,期望得分 20 ,结合算法二、三可以获得 60 分。
算法五
观察 m=2 的情形并推广,我们发现如下性质:如果 a_i=a_j ,那么第 i 局与第 j 局游戏结束时,桌面上的点数序列相同。
不妨设 i<j 。第 i 局游戏中,桌面上最左侧牌的点数始终为 a_i ,尽管这张牌本身可能被后来打出的同点数牌替换。当打出第 j 张牌时,由于 a_j=a_i ,桌面上原有的牌会被全部收走,再放上新打出的牌,此时只剩一张点数为 a_j 的牌。这与第 j 局游戏打出第一张牌后的状态相同,此后两局打出的牌也完全相同,因此最终点数序列相同。
也就是说,对于每种点数,以其作为第一张牌点数的所有游戏结果完全相同。我们只需为每种点数选一局游戏进行模拟,例如选择从其最后一次出现的位置开始的那一局。设点数 x 在原序列中出现了 \mathrm{cnt}_x 次,则模拟从 x 开始的游戏后,给最终留下的每种点数的答案加上 \mathrm{cnt}_x 。一共只需模拟 m 局游戏,可以得到一个 \mathcal{O}(nm) 的算法。
该算法视实现可以通过前 17 个测试点到前 19 个测试点,期望得分 68\sim76 。结合算法三可以获得 72\sim 84 分。
算法六
基于算法五的结论,假设 a_i 是该点数在序列中的最后一次出现,且 i<n ,我们继续观察第 i 局与第 (i+1) 局游戏之间的关系。由于后续不会再打出点数为 a_i 的牌,这张牌始终留在最左侧,右侧的操作与第 (i+1) 局完全相同。因此,第 i 局游戏的最终点数序列,即为第 (i+1) 局游戏的最终点数序列前面插入一个 a_i 的结果。
现在考虑建立 m 个节点,编号依次为 1,2,\ldots,m ,每个节点对应一种点数。对于每种点数 x ,设 p=\mathrm{last}_x ;若 p<n ,则令节点 x 的父亲为 a_{p+1} 。点数 a_n 对应的节点没有父亲,作为根。
由于 \mathrm{last}_{a_{p+1}}\ge p+1>\mathrm{last}_x ,沿父亲方向,点数最后一次出现的位置严格递增,因此不会形成环,最终一定到达根 a_n ,这些边构成一棵树。
由上述结论,以点数 x 开始的游戏,其最终点数序列就是从节点 x 开始一直向上到根的路径。因此,点数 x 会在以其子树内任意节点对应的点数开始的游戏中留下。
将节点 x 的权值赋为点数 x 在序列中的出现次数 \mathrm{cnt}_x ,则其子树内所有节点的权值和就是答案。用一次遍历统计即可。
该算法的时间复杂度为 \mathcal{O}(n+m) ,可以通过所有测试点,期望得分 100 分。