杂题选写 2
CF1423L Light switches
*2600
tag:暴力枚举,meet in middle
题目描述
有 \(n(1\le 1000)\) 盏灯和 \(s(1\le s\le 30)\) 个开关,每个开关能控制一些灯,并改变这些灯的状态。
接下来有 \(d(1\le d\le 1000)\) 次询问,每次询问给出 \(k_i\) 盏亮着的灯,询问将所有灯全部熄灭最少需要按几次开关。
思路
每个开关至多会被按一次。
注意到 \(s\) 很小。
可以考虑一个 \(O(2^s)\) 的暴力。
但复杂度显然不对。
所以 meet in middle,将开关划分为 \(k\) 和 \(s-k\) 两部分。
每次询问枚举 \(k\) 中的所有状态,并在 \(s-k\) 的部分找其与初始状态异或后的状态。
具体实现时可以用 unordered_map 存下 \(s-k\) 这部分的所有状态,灯的状态可以用 bitset 来存。
时间复杂度 \(O(2^k+2^{s-k}+\frac{dn2^k}{w})\),还要带 unordered_map 的常数。
在 \(k\) 取 \(12\) 左右可以通过。
CF1942F Farmer John's Favorite Function
*2700
tag: 数据结构(线段树,分块)。
题目描述
给定一个长度为 \(n(1\le n\le 2\cdot10^5)\) 的序列 \(a\),并定义函数 \(f(x)\):
有 \(q(1\le q\le 2\cdot10^5)\) 次操作,每次操作 \(a_x\leftarrow y\),求出每次操作后的 \(\lfloor f(n)\rfloor\)。
思路
发现我们每次直接将 \(f(x)\) 操作完过后向下取整,其值与 \(\lfloor f(x)\rfloor\) 相等。
也就是说我们可以只计算每次开根向下取整后的值。
考虑将修改离线下来,跑一边扫描线,来维护到了第 \(i\) 个位置 \(1\sim q\) 这些时刻的 \(f(i)\)。具体的,我们可以将每个位置上的数存在的时间区间求出来,每次将其存在的区间整体加。至多只有 \(O(n+q)\) 个区间。
现在我们只需要维护两种操作:区间加和全局开根。
这是一个经典的问题:用势能线段树来维护。
具体的,记录一个区间的最大值 \(mx\) 和最小值 \(mi\),若 \(\lfloor\sqrt{mx}\rfloor-mx=\lfloor\sqrt{mi}\rfloor-mi\),那么就可以区间整体加。否则直接暴力往下递归。
CF1945H GCD is Greater
*2600
tag:数学,位运算,暴力枚举。
题目大意
给定 \(n(4\le n\le 4\cdot10^5)\) 个数 \(a_i(1\le a_i\le 4\cdot10^5)\),将 \(a_i\) 分成两个大小不小于 \(2\) 的集合 \(A,B\),使得 \(A\) 集合中所有数的 gcd 大于 \(B\) 集合中所有数按位与加 \(x(1\le x\le 4\cdot10^5)\) 的值。或报告无解。
思路
多选数 gcd 不会变大,少选数按位与不会变小,所以 \(A\) 集合可以只选择两个数。
接下来让我们按位考虑 \(B\) 集合的答案。若存在三个及以上的数在这一位是 \(0\),那么这一位必然是 \(0\)。
否则只会有 \(1\) 个或 \(2\) 个数在这一位上是 \(0\),我们记这些数组成的集合为 \(S\)。若要使得这一位是 \(0\) 那么 \(A\) 集合中就不能够全部选中这些数;若为 \(1\) 则 \(A\) 集合必须包含这些数。
\(A\) 集合在一个 \(S\) 中的数都不选的情况下,\(B\) 集合的值是确定的,我们只需要在剩下的数中最大化 \(A\) 集合的值,可以通过枚举 gcd 来解决,调和级数时间复杂度是 \(O(V\log V)\) 的。
否则 \(A\) 中必然包含至少一个集合 \(S\) 中的数。集合 \(S\) 的大小只有 \(O(\log V)\),所以我们可以枚举集合 \(S\) 中的每一个数,然后再 \(O(n)\) 枚举另一个数,计算区间按位与可以使用 st 表来维护,这部分的时间复杂度为 \(O(n\log^2V)\),另一个 \(\log V\) 来自计算 gcd。
CF749E Inversions After Shuffle
*2400
tag:概率期望
题目大意
给定一个 \(n(1\le n\le 10^5)\) 的排列 \(p\)。随机选取一个 \(p\) 的子段并随机打乱。求出打乱后 \(p\) 的期望逆序对数量。
思路
去考虑每个可能的区间不太好做。让我们考虑可能的两对数 \(i,j(i<j)\) 对初始序列造成的影响。
若 \(i,j\) 同时在一个被打乱的区间内,那么它们有 \(\frac{1}{2}\) 的概率改变先后顺序,而它们在同一个可能被打乱的区间内的概率为 \(\frac{2(n-j+1)i}{n(n+1)}\)。
也就是说,若 \(a_i<a_j\) 那么它们会造成 \(\frac{(n-j+1)i}{n(n+1)}\) 的贡献,否则造成 \(-\frac{(n-j+1)i}{n(n+1)}\) 的贡献。
可以通过树状数组来维护。
CF303C Minimum Modular
*2400
tag:暴力枚举,同余
题目描述
给定 \(n(1\le n\le 5000)\) 个互不相同的整数 \(a_1,a_2,\dots,a_n(0\le a_i\le 10^6)\) 和整数 \(k(0\le k\le 4)\)。求出一个最小的 \(m\) 使得可以通过删除 \(k\) 个数满足 \(a\) 中所有数模 \(m\) 的值互不相同。
思路
\(m\) 的最大值只有 \(10^6+1\)。
不妨来枚举 \(m\)。
将两对整数 \(a_i\) 与 \(a_j\) 同余的条件变为 \(m\mid (a_i-a_j)\)。
显然,若每次同余的对数大于了 \(\frac{k(k+1)}{2}\) 那么这个 \(m\) 一定无解。
否则,可能相同的数至多只有 \(\frac{4\times 5}{2}=10\) 个,我们枚举这 \(10\) 个数即可判断 \(m\) 是否符合条件。
枚举倍数根据调和级数时间复杂度为 \(O(V\log V)\),总时间复杂度为 \(O(\frac{k(k+1)}{2}V\log V)\)。
CF1374F Cyclic Shifts Sorting
*2400
tag:构造,思维
题目描述
给定一个长度为 \(n(3\le n\le 500)\) 的数组 \(a_1,a_2,\dots,a_n(1\le a_i\le 500)\)。
定义一次 swap 操作为选择一个整数 \(i(1\le i\le n-2)\),然后将 \(a_i,a_{i+1},a_{i+2}\) 循环位移,也就是 \(a_i:=a_{i+2},a_{i+1}:=a_{i+1},a_{i+2}:=a_{i+1}\)。
你需要使用至多 \(n^2\) 次 swap 操作将数组 \(a\) 排序,或报告无解。
思路
一种不是正解的正确做法: 可以类似于选择排序,先从前往后排再从后往前排即可。
一个基本的想法是我们去做类似于选择排序的排序至少可以将前 \(n-2\) 个数排好序。
那么若 \(a_{n-1}\le a_n\) 那么整个数组就已经拍好序了。否则 \(a_{n-1}>a_n\),那这种情况是否就无解了?
也不一定。实际上只要数组中存在两个相同的数就一定有解。
为什么?让我们从逆序对的角度来考虑。若我们交换三个不同的数,那么逆序对的数量要么会改变 \(2\) 要么不变。然而,若交换的数中仅有两个相同,那么逆序对数量会改变 \(1\) 或 \(2\)。
也就是说,我们可以通过交换两个相同的数来改变逆序对数量的奇偶性,从而构造出答案。
那么构造方案也就呼之欲出了:
-
若 \(a_{n-2}=a{n}\),那么 swap \(a_{n-2}\)。
-
否则找到一个整数 \(p\) 满足 \(a_{p}=a_{p+1}\) 且 \(a_{p}\not=a_{p+2}\),然后 swap 两次 \(a_p\),之后再做一次最开始的排序。
「COI 2019」IZLET
tag: 构造。
题目大意
有一棵未知的有 \(n\) 个节点的树,每个节点有一个颜色。给定一个 \(n\times n\) 的矩阵 \(a\),其中 \(a_{i,j}\) 表示从 \(i\) 到 \(j\) 的路径上有 \(a_{i,j}\) 种不同的颜色。
构造出一棵满足条件的树。
思路
首先对于两个点 \(u,v\),若 \(a_{u,v}=1\),那么它们的颜色一定相同。并且,若我们先将它们连上边,那么答案一定不劣。
其次,若 \(a_{u,v}=2\),那么我们也可以将它们连上边。
此时我们发现,仅靠 \(1\) 和 \(2\) 已经足够得出树的形态了。
把这棵树建出来,接下来考虑确定每个点的颜色。
可以枚举点 \(x\) 然后找到所有与 \(x\) 颜色相同的点。
对于一个点 \(y\),记在从 \(x\) 到 \(y\) 的路径上与 \(x\) 颜色相同且离 \(y\) 最近的点为 \(lst\),\(lst\) 的下一个点为 \(nxt\),那么 \(y\) 与 \(x\) 颜色相同当且仅当 \(a_{lst,y}=a_{nxt,y}\)。
「COI 2019」TENIS
tag:思维,数据结构。
题目大意
思路
首先,所有排名为 \(1\) 的人都可以获胜,将他们加入一个集合当中。
其次,若可以打败能够获胜的人,那么也可以获胜。
我们可以模拟这个过程来在 \(O(n)\) 的复杂度内解决这个问题。
若我们选了一个人,实际上是将他在三种场地上所有能赢他的人都能获胜,以此类推。
我们实际上是要找到一个最小的 \(i\) 满足,对于在前 \(i\) 列出现过的所有人他们最后一次出现仍然在前 \(i\) 列。
维护每个人最后出现的位置 \(r_i\),那么这个人对 \([r_i,n]\) 都有加 \(1\) 的贡献,我们只需要找到一个位置满足贡献和等于下标。
可以先将每个位置设为负的下标,然后找到第一个为 \(0\) 的位置即可。