杂题选写 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\) 的位置即可。
CF1930E 2..3...4.... Wonderful! Wonderful!
*2400
tag:组合计数
题目大意
现有一个长度为 \(n(n\le 10^6)\) 的数列 \(a\) 满足 \(a_i=i\)。
给定一个 \(k\),你可以进行若干次如下操作:选择 \(a\) 序列中一个长为 \(2k+1\) 的子序列,并删除它的前 \(k\) 个数和后 \(k\) 个数。
对于每一种 \(k\),求出可以得到的序列个数,对 \(998244353\) 取模。
思路
一个多月没写过了,现在来补一点。
相当于我们需要删除一些位置,我们不在意过程,只关心最后得到的序列。
那么一个最终序列合法的充要条件就是:
- 被删除的位置的个数为 \(2k\) 的倍数。
- 存在一个没被删除的位置其左侧或右侧存在 \(k\) 个被删除的位置 \(p\)。
必要性显然,证一下充分性。如果 \(p\) 的一侧存在大于 \(3k\) 个被删除的位置,那么我们可以删除 \(2k\) 个。最后两边都剩下 \([k,3k)\) 个位置,并且两边之和都是 \(2k\) 的倍数。
所以,考虑去枚举操作次数 \(c\),用总方案数减去不合法方案数。那么不合法的方案就需要满足除开最左侧和最右侧的 \(k-1\) 个被删除的位置,其它被删除的位置都是连续的。我们将中间连续的位置看成一个,就可以计算出答案。
时间复杂度 \(O(n\ln n)\)。
CF2026F Bermart Ice Cream
*2700
tag:动态规划,势能分析
题目描述
Bermart 连锁店出售各种冰淇淋,每种冰淇淋都有两个参数:价格和口味。
最初,有一家编号为 \(1\) 的商店,不出售任何产品。
您必须处理以下类型的 \(q\) 个查询:
1 x:新店开张,编号为开张前的最大编号 \(+1\),出售与 \(x\) 店相同种类的冰淇淋,且顺序与 \(x\) 店相同。2 x p t:一种价格为 \(p\)、口味为 \(t\) 的冰淇淋在 \(x\) 店上市。3 x:\(x\) 店中供应时间最长(最早出现)的一种冰淇淋被移除。4 x p:求在 \(x\) 店出售的所有种类的冰淇淋中花费不超过 \(p\) 元能获得的最大总美味度,每种冰淇淋只能购买一次。
\(1\le q\le 3\times 10^4\),\(1\le p,t\le 2\times 10^3\)。
思路
相当于可持久化前删后加的 01 背包,还是比较套路的。
先解决可持久化,这个可以直接建出版本树。
现在在前面后和后面都有添加和删除操作,考虑维护两个栈分别处理前后的操作。只需要在其中一个栈为空时重构两个栈,将元素两两平分给两个栈。
时间复杂度是 \((qV)\) 的。
令 \(c_i\) 表示第 \(i-1\) 次和第 \(i\) 次重构之间加入的元素个数,令 \(s_i\) 表示第 \(i\) 次重构时一共元素的个数,那么有 \(s_i=\frac{s_i}{2}+c_i\),每个 \(c_i\) 造成的贡献就是 \(c_i+\frac{c_i}{2}+\frac{c_i}{4}+\dots\) 一共就是 \(O(q)\)。
CF2096F Wonderful Impostors
*3100
tag: 数据结构,贪心,双指针
题目描述
你是一位名为 Gigi Murin 的主播。今天,你将与编号为 \(1\) 到 \(n\) 的 \(n\) 位观众进行一场游戏。
在游戏中,每位玩家要么是船员,要么是内鬼。你并不知道每位观众的具体身份。
共有 \(m\) 条编号为 \(1\) 到 \(m\) 的陈述,每条陈述要么为真,要么为假。对于从 \(1\) 到 \(m\) 的每条 \(i\),陈述 \(i\) 属于以下两种类型之一:
- \(0\:a_i\:b_i\)(\(1 \leq a_i \leq b_i \leq n\))——观众 \(a_i, a_i + 1, \ldots, b_i\) 中没有内鬼;
- \(1\:a_i\:b_i\)(\(1 \leq a_i \leq b_i \leq n\))——观众 \(a_i, a_i + 1, \ldots, b_i\) 中至少存在一名内鬼。
你需要回答 \(q\) 个形式如下的问题:
- \(l\:r\)(\(1 \leq l \leq r \leq m\))——是否有可能使得编号 \(l, l + 1, \ldots, r\) 的所有陈述同时为真?
注意:不保证所有观众中至少存在一名内鬼,也不保证所有观众中至少存在一名船员。
思路
先考虑对于一次询问我们可以如何判断。
将询问区间内所有的 0 类型的条件拿出来,并将它们所对应的区间加 \(1\)。之后看每一个 1 类型的条件,要求在它对应的区间里存在至少一个位置为 \(0\),即不会被若干 0 类型的条件的并所包含。
接下来考虑多次询问怎么做。如果对于每一个 \(i\) 我们可以求出一个最小的 \(j\) 使得区间 \([j,i]\) 中的条件都满足限制,那么对于一个 \(r=i\) 的询问,只需要满足 \(j\le l\) 即可。
让我们考虑用双指针来维护。当我们新加入一个条件 \([L,R]\) 后:
-
若为 0 类型,那么它可能自己或与左右的区间合并起来来使得一个 1 类型的条件被包含。注意到,这样 1 类型的条件一定与当前新加入的 0 类型条件有交,否则它会在之前就被判断掉。所以,我们每次可以求出 \([1,L]\) 中最后一个为 \(0\) 的位置 \(ql\) 和 \([R,n]\) 中第一个为 \(0\) 的位置 \(qr\),之后就只需要看是否存在一个 1 类型的条件被 \([ql+1,qr-1]\) 包含,被包含就说明现在不合法,左指针需要增加。
-
若为 1 类型,要不合法就只可能是它被若干 0 类型条件的并所包含,若是其它 1 类型的条件不合法也会在之前就被判断掉。当然,这个也很好判断,只需要看 \([L,R]\) 的最小值是否为 \(0\),若不为 \(0\) 就说明现在不合法,左指针需要增加。
说一些实现上的事情。对于 0 类型的条件,可以使用线段树维护区间加减,区间 min 和区间最左边及最右边的最小值位置,这些都比较常规。对于 1 类型的条件,同样使用线段树维护每个右端点所对应的左端点的最大值,由于有加有删,所以可以在每个叶子上用一个 multiset 来维护存在的值。这样,对于每次询问 \([ql+1,qr-1]\) 中是否有 1 类型的条件,就只需要看区间 \([ql+1,qr-1]\) 中最大的左端点是否大于等于 \(ql+1\)。
时间复杂度 \(O(m\log n)\)。

浙公网安备 33010602011771号