思维题练习集

这里是一些简单思维题的练习集合,主要针对 A B C 做题较慢而总结一些重点题目的思路。本部分的练习原则是如果一段时间内没有思考出有效做法就看题解的方法,并总结思维中的问题 qaq

其中每道题题号 \(\color{green} \circ\) 表明未看题解即通过,而 \(\color{red} \circ\) 则表明参考了题解。

\[\newcommand{\lvert}{\left\vert} \newcommand{\rvert}{\right\vert} \rule{750px}{1px} \]

\(\color{green} \circ\) CF1579B. Shifting Sort

Description

给定一个长度为 \(n\) 的序列 \(\{a_n\}\),每一次你可以选择两个位置 \(l, r\) 和一个数字 \(d\),将 \(a_{l \cdots r}\) 这个子序列循环左移 \(d\) 位,求构造一种不多于 \(n\) 步左移的将排列排成升序的方案。\(t\) 组数据。

数据范围:\(1 \le t \le 1000, 2 \le n \le 50, 1 \le a_i \le 10^9.\)

Solution

本题因为感觉模拟好像比较麻烦而一直不知道如何写,又因为读成右移而一直 WA,最后才解决了问题。

考虑到操作次数不多于 \(n\) 次,故只需考虑枚举排名为 \(i\) 的数并将其移动到正确位置即可。我们只需维护当前枚举到的数应当去的位置并且模拟每次左移即可。这里 \(n\) 很小,离散化直接使用了 map 并且每次暴力寻找排名为 \(i\) 的数的位置。

Code

View on github


\(\color{red} \circ\) CF1592B. Hemose Shopping

Description

给定一个长度为 \(n\) 的序列 \(\{a_n\}\),每一次你可以选择两个位置 \(i, j\),然后交换这两个位置上的数。但是 \(i, j\) 需满足 \(\lvert i - j \rvert \ge x\),其中 \(x\) 已给定。问是否存在一种方案将序列排为升序。\(t\) 组数据。

数据范围:\(1 \le x \le n \le 10^5, \sum n \le 2 \times 10^5, 1 \le t \le 10^5.\)

Solution

本题如果暴力模拟肯定会超时,而如果优化模拟似乎过于麻烦,考虑其他方法。然而因为调试模拟花了过多精力,无力优化,于是直接看了题解。

我们考虑一个位置 \(i\),如果其距离 1 或者 \(n\) 的距离都小于 \(x\),则我们始终无法移动这个数,除非这个数已经到了它该到的位置上,否则显然无解。

我们考虑什么时候这种位置会存在,发现 \(2x \le n\) 时这个位置总是不存在的。而对于 \(2x > n\) 的情况,我们发现 \(a_{n - x + 1}, a_{n - x + 2}, \cdots, a_x\) 是无法移动的。所以如果它们在排序中需要移动位置,则无解。

Code

View on github


\(\color{green} \circ\) CF1579D. Productive Meeting

Description

给定一个长度为 \(n\) 的自然数序列 \(\{a_n\}\),每一次你可以选择其中两个非零数,让这两个数各自减 1,求这种操作最多可以被执行多少次,并且输出方案。\(t\) 组数据。

数据范围:\(1 \le t \le 1000, 2 \le \sum n \le 2 \times 10^5, 0 \le \sum a_i \le 2 \times 10^5\)

Solution

通过手模我们似乎可以得出一个结论,每一次选择两个较大数自减要比两个较小数自减更优。我们考虑为什么会是这样,我们发现最终无法执行操作的情况一定是全为 0 或只剩一个非 0 数。显然对于只剩一个非 0 数的情况,我们总是希望这个数尽可能小。所有我们优先消耗当前序列中最大的两个,即每次都取最大的两个数进行一次操作。注意两个数进行一次操作后,序列的最大值可能会变化,所以我们每次取数仅执行一次操作后就要重新判断最大值。

Code

View on github


\(\color{red} \circ\) CF1594D. The Number of Imposters

Description

\(n\) 个人,他们其中一些人是只会说谎话的 \(\textrm{imposter}\),剩下的则是只会说真话的 \(\textrm{crewmate}\)。现在给他们从 1 到 \(n\) 标号,为了找出谁是说谎话的人,他们在一次讨论中一共说了 \(m\) 句话,我们将这些话记作编号为 \(i\) 的人说编号为 \(j\) 的人是 \(\textrm{imposter} / \textrm{crewmate}\)。现在问这 \(n\) 个人中最多可能有多少个 \(\textrm{imposter}\),如果他们说的话互相矛盾,则输出 -1。\(t\) 组数据。

数据范围:\(1 \le t \le 10^4, 1 \le \sum n \le 2 \times 10^5, 0 \le \sum m \le 5 \times 10^5.\)

Solution

看到题中有描述某两人的某种关系,所以考虑建图来维护他们之间的逻辑关系,我们发现我们只要指定一个人是 \(\textrm{crewmate}\),我们就可以推出其所在连通分量的人是什么身份,或者推出一个矛盾的情况。并且我们发现,如果我们指定一个是 \(\textrm{crewmate}\) 所推出的结果和指定其是 \(\textrm{imposter}\) 推出的结果是恰好相反的,由于我们只需统计数量,所以我们只需取两种人中最大的加进答案中即可。

我们考虑如何通过一个人说的话推出其与他人的关系。这就需要发现一个比较重要的性质,如果一个人说另一个人是 \(\textrm{imposter}\),那么这个人和另一个人不是同类;反之,如果一个人说另一个人是 \(\textrm{crewmate}\),那么这个人和另一个人一定是同类。不难发现我们可以通过异或的性质很好的计算一个人的属性。如果两次遍历到同一结点时,计算表明两次计算此人属性不相同,则出现了矛盾。

本题主要卡在了如何通过一个人说的话推出与其与他人的关系,其实这个结论也不是特别难发现,当然,如果直接使用种类并查集也可以做,但那似乎是我很久没有复习的内容,这个题也提醒了我复习此算法。

Code

View on github


\(\color{green} \circ\) CF1594C. Make Them Equal

Description

给定一个长度为 \(n\) 的字符串 \(s\) 和一个字符 \(c\)。每一次操作你可以选择一个数 \(x\),然后对于所有不能整除 \(x\)\(i\),将所有满足条件的位置 \(i\) 上的字符替换为 \(c\)。求将字符串中所有字符全部变成字符 \(c\) 的最小操作次数。\(t\) 组数据。

数据范围:\(1 \le t \le 10^4, 3 \le n \le 3 \times 10^5.\)

Solution

我们首先可以特判字符串 \(s\) 是否全部字符均为字符 \(c\)。如果是,则答案为 0,否则我们就需要进行以下讨论。

由于要求最小操作次数,所以每次操作我们总是希望字符串中,仅可能改变更多位置上的字符。我们发现对于我们选定的 \(x\),只有 \(x\) 的倍数上的位置上的字符不会被替换。而我们发现对于 \([\lfloor \frac {n} {2} \rfloor + 1, n]\) 所有数,除了其本身,在 \([1, n]\) 上没有其他倍数。故如果对于一个 \(i \in [\lfloor \frac {n} {2} \rfloor + 1, n]\),其位置上的字符为 \(c\) 则答案显然为 1。否则考虑选择了这个位置后,除了这个位置的其他位置均变成了字符 \(c\),这时我们再选择一个 \([\lfloor \frac {n} {2} \rfloor + 1, n]\) 内的数,故此时答案为 2。

Code

View on github


\(\color{red} \circ\) CF1592C. Bakry and Partitioning

Description

给定一个 \(n\) 个结点的树,其中编号为 \(i\) 结点的权值为 \(a_i\),你现在可以最少可以删除 1 条边,最多可以删除 \(k - 1\) 条边。如果删除了若干条边,会生成 \(k\) 个连通块。问是否存在一种方案使得所有连通块内的所有结点异或和相等。\(t\) 组数据。

数据范围:\(1 \le t \le 5 \times 10^4, 2 \le k \le n \le 10^5, \sum n \le 2 \times 10^5.\)

Solution

显然我们可以将树的连通块数量为 2 到 \(k\)。为了方便讨论,我们设全体结点权值的异或和为 \(S\),对于分成 2 个连通块的情况,设这两个连通块的异或和均为 \(x\),则有 \(x \oplus x = S\),由异或的性质可知 \(S = 0\)。故如果 \(S = 0\) 则方案一定存在,且可以断掉任意一条边。

考虑对于分成 3 个联通块的情况,设这三个连通块的异或和均为 \(x\),则有 \(x \oplus x \oplus x = S\)\(x = S\),则我们需要找到一棵子树的异或和为 \(S\) 后,将这颗子树去掉后再判断是否还有另一个子树异或和为 \(S\),如果有的话,就存在一种方案。

注意如果我们找到一个子树异或和为 \(S\) 后,去掉这颗子树后,却不能另一个异或和为 \(S\) 的子树,我们担心会不会是因为我们第一个子树选的不合适导致的。然而这是不可能的,我们其实只需证明任意两种划分方式是可以相互转化的。我们考虑如果存在一种方案使得将这颗树分成三个连通块 \(V_1, V_2, V_3\) 且各块异或和为 \(S\),假设我们找到了另一种划分方式,将树划分为三个连通块 \(V_1', V_2', V_3'\),显然对于这两种划分方式,对于每一个 \(V\) 中的下标 \(i\),都会有一个 \(V'\) 中的下标 \(j\) 与之对应使得有 \(V_i \subseteq V_j'\)\(V_j' \subseteq V_i\) 成立。不妨设 \(V_i \subseteq V_j'\),我们由于 \(\bigoplus_{u \in V_i} a_u = S, \bigoplus_{u \in V_j'} a_u = S\),故有 \(\bigoplus_{u \in (V_j' - V_i)} = S \oplus S = 0\)。故 \(V_j' - V_i\) 这一部分的异或和为 0。所以这一部分无论分到剩下的哪一个连通块中,我们总可以找到一种合法的划分方式。

对于 \(k \ge 4\) 的情况,我们发现对于偶数个连通块,我们都可以将其转化为连通块为 2 的情况。对于奇数个连通块,我们都可以将其转化为连通块为 3 的情况,所以我们只需判定连通块为 2,3 的情况即可。

本题当时想到了以上结论,但在实现上不知道如何去掉一棵子树,于是考虑的非常复杂,看完题解的代码发现其实很好实现,只需在计算子树异或和时如果发现当前节点的一个孩子的子树异或和为 \(S\),就不将这个孩子的子树计入当前结点的异或和即可。

Code

View on github


\(\color{red} \circ\) CF1600E. Array game

Description

给定长度为 \(n\) 的一个序列 \(\{a_n\}\),Alice 和 Bob 在玩这样一个游戏:游戏由若干次操作组成,每一次操作都要从序列 \(\{a_n\}\) 的开头或结尾取出一个数,两个人轮流操作,但每一次取数都要比上一次对方取的数大(先手第一次取数没有限制),且最后可以取数的人获胜。假设两个人都会按自己的最优策略操作,Alice 先手,问对于给定的序列,谁会获胜?

数据范围:\(1 \le n \le 2 \times 10^5, 0 \le a_i \le 10^9.\)

Solution

本题的关键在于“每一次取数都要比上一次对方取数大”这个限制,我们发现这个限制其实很强,重点在于如果一个人选择的序列一边的数时,如果另一边的数比一边的数小,则之后两人只能在此边取数。这时的输赢只在于这一边的最长的连续递增序列的长度是多少。

所以由上分析,先手就掌握了选择的主动权。我们不妨设初始序列左边比右边大,则我们发现如果序列左边最长连续递增序列的长度为奇数,则先手就直接选择取左边,此时先手必胜。

但如果我们发现序列的右边最长连续递增序列的长度为偶数,则先手为了胜,会选择取右边,而显然后手为了胜也不会取左边。这时的答案取决于右边的最长连续递增子序列的奇偶性。

于是我们得出结论,若左右最长连续递增子序列有一个长度为奇数,则 Alice 胜,否则 Bob 胜。

然而我写本题时,不知道如何研究结论还以为是一个不易找结论的博弈论题,只写出了暴力搜索,如果刻意构造数据应该时间复杂度能到 \(O(n^2)\) 左右。所以直接被卡掉。

Code

View on github


\(\color{red} \circ\) CF1606E. Arena

Description

在竞技场上有 \(n\) 个英雄,其中第 \(i\) 个英雄的生命值为一个整数 \(a_i\),他们在竞技场上展开一场战斗。

战斗以回合制方式进行,每一回合每一位英雄都会对场上所有其他英雄发动攻击,并且使场上所有其他英雄的生命值减 1,在所有英雄攻击在该回合的攻击结束后,生命值为非正数的英雄被淘汰离场。问对于给定的英雄个数 \(n\) 和英雄的生命值的最大不要超过 \(x\) 的要求,有多少种可能的英雄生命值的分配方案,使得最终没有任何英雄留在场上?答案对 998244353 取模。

注意每个英雄被视作本质不同的,比如说当 \(n = 4\),这时 \([1, 2, 1, 1]\)\([1, 1, 2, 1]\) 被视为两种不同的英雄生命值的分配方案。

数据范围:\(1 \le n \le 500, 1 \le x \le 500.\)

Solution

本题为递推计数的题目,我们就考虑 \(f(i, j)\) 表明场上 \(i\) 个英雄,且生命值最大不超过 \(j\) 的方案数。

我们发现当 \(j < i\) 时,一回合后,场上每一个英雄都会被淘汰,所以我们只需考虑给他们的生命值分配 \([1, j]\) 之间的数即可。所以这时的方案数即为 \(j^i\)

\(j \ge i\) 时,除了所有英雄在下一回合全部淘汰的可能,即 \(f(i, i - 1)\) 的情况,我们发现有一些英雄可能不会被淘汰,这时考虑枚举下一回合会淘汰 \(k\) 个人,这时我们就要挑出这 \(k\) 个人并且将其的生命值分配 \([1, i - 1]\) 之间的数即可,故方案数为 \(\dbinom{i}{k} (i - 1)^k f(i - k, j - (i - 1))\)。显然如果要淘汰 \(i - 1\) 个人时,最终场上会只留下 1 名英雄,这时他无法被淘汰,所以这样的状态是不合法的,所以 \(k \in [0, i - 2]\)

至此,我们就可以将 \(f(i, j)\) 的递推式写出来了:

\[f(i, j) = \begin{cases} j^i & j < i \\ f(i, i - 1) + \sum_{k = 0}^{i - 2} \limits \dbinom{i}{k} (i - 1)^k f(i - k, j - (i - 1)) & j \ge i \end{cases} \]

答案即为 \(f(n, x)\)

感觉自己仍然不是特别会找递推式,递推式的关键有时在于更新状态时的关系,而不关心是如何更新的。

Code

View on github


\(\color{red} \circ\) ARC131C. Zero XOR

Description

桌子上有 \(n\) 个饼干,第 \(i\) 个饼干上写有数字 \(a_i\),且各个 \(a_i\) 互不相同。你和你的朋友正在玩一个游戏,游戏内容是两人轮流选择一个饼干然后吃掉它,如果吃掉饼干后,剩余饼干上的数字异或和为 0,那么最后吃饼干的人获胜。如果你是先手,问你是否有必胜策略。

数据范围:\(1 \le n \le 4 \times 10^5, 1 \le a_i \le 1 \times 10^9.\)

Solution

本题又是一道博弈论题,一直不知道是怎样的一个规律,看到题解才明白大概应该是一个什么样的思路。

考虑一个比较神奇的结论:在 \(n\) 为奇数时,本游戏不可能在两人吃掉的饼干数之和为偶数时结束。要说明这一点,我们只需证明 \(n\) 为奇时,游戏不可能在两人各吃掉一个饼干后结束即可。我们考虑如果先手选择的饼干是 \(x\),如果存在一个 \(y\) 使得 \(a_x \oplus a_y\) 等于所有饼干的异或和,那么后手就必胜(当然如果先手选 \(y\),后手可以选 \(x\),使得其必胜)。容易证明对于一个特定 \(a_x\),满足此条件的数 \(b\) 是唯一的。考虑到满足这样条件的数对 \((a_x, a_y)\) 只能有 \(\lfloor \frac {n} {2} \rfloor\) 个,那么 \(n\) 为奇数时,总能找到一个 \(x\),使得 \(a_x\) 不在任何的数对中。由此数学归纳可知,本游戏不可能在两人吃掉的饼干数之和为偶数时结束。而这说明游戏只能在奇数次操作后结束,那么就意味着 \(n\) 为奇数时,先手必胜。

再考虑 \(n\) 为偶数的情况,显然如果先手吃掉一个饼干,如果其没有获胜,则游戏就变成了 \(n\) 为奇数时的情况,此时显然原来的后手必胜。所以此时先手只有能一步取胜,才有必胜策略。

Code

View on github

posted @ 2021-10-20 00:06  Nickel_Angel  阅读(278)  评论(0编辑  收藏  举报