Manthan, Codefest 19
A. XORinacci
Description
定义一个函数(f(n))
现在给定(a, b, n),你需要求出(f(n))的值
(a, b, n leq 10^9)
Solution
我们发现这个函数的最小循环节为(3),所以我们直接对于(n mod 3)分类讨论一下就可以了
B. Uniqueness
Description
给定一个长度为(n)的序列,你可以选择一个区间中的数字删除,你要使得剩下的数字两两不同。问最少需要删除多少个数字
(n leq 2000)
Solution
我们发现删除之后需要保证每种数字最多出现一次,所以当删除区间的往右移动的时候,左端点肯定不会往左移动。所以左端点关于右端点单调,所以我们就(two~point)就好了,另外我们需要再开一个桶维护剩下的数字的个数
C. Magic Grid
Description
有一个(n * n)的矩阵,你需要把数字(0)到(n^2 - 1)这(n^2)个数字不重不漏地填入其中,你需要保证每行每列的异或值相同,请求出任意一种合法的方案
(n leq 1000),并且(n)为(4)的倍数
Solution
有两个关于异或的性质
-
对于任意自然数(k),均满足(4k oplus 4k + 1 oplus 4k + 2 oplus 4k + 3 = 0)
-
对于任意自然数(k),均满足(k oplus k + 4 oplus k + 8 oplus k + 12 = 0)
所以,我们就可以直接构造了,大概就像下面这个样子
D. Restore Permutation
Description
有一个长度为(n)的(1)到(n)的排列(p_i),我们定义(s_i = sumlimits_{j = 1}^{i - 1} [p_j < p_i] cdot p_j)
现在告诉你(s_i)序列,你需要倒推出(p_i)序列
(n leq 2 imes 10^5)
Solution
我们可以考虑按(p_i)从小到大依次还原每一个数字。首先我们看当前(s_i)有哪些位置为(0),那么最后一个(s_i)为(0)的位置(pos)就一定是当前需要还原的数字。然后把这个位置扔掉,再对区间([pos + 1, n])里的每一个(s_i)都减去当前还原的数字
我们考虑为什么这样子做是对的,我们发现如果比一个数字要小的所有数字都被删除了,那么当前需要还原的数字显然就是这个数字了。当有多个位置的(s_i)为(0)时,我们要优先考虑后面的位置。因为接下来要填的几个数字都不相同,我们要让它们互相没有影响,就只能倒序填入每一个位置
E. Let Them Slide
Description
有一个(n)行(w)列的棋盘,每一行都有一个长度为(l_i)的滑块,滑块上的每一个位置都有一个权值(a_{i, j})
现在你可以随意平移所有滑块(不能移动到别的行,也不能超出棋盘的边界),问你对于第(forall j in [1, w])列可能出现最大的权值和为多少,询问之间是独立的
(1 leq n, w, sum l_i leq 10^6, l_i leq w, |a_{i, j}| leq 10^9)
Solution
我们可以考虑每一行的滑块对每一列的贡献,我们发现对于任意一个数字能够影响到的都是一段区间,如果一个位置被多个区间包含,我们就取权值最大的。这样当前滑块对于所有列贡献不同的区间就只有(mathcal{O}(l_i))个,我们就直接把这些贡献加到答案里面就可以了
我一开始还思博了,用了一个树状数组来维护贡献,后来发现直接差分就可以了,因为它只要最后求一遍答案
在求每一个滑块对每一个区间的影响时,我是直接用(set)搞的,实际上可以直接用一个双端队列去搞。因为每一个数字的影响区间长度都相同
F. Bits And Pieces
Description
给定一个长度为(n)的序列(a_i),定义一个三元组((i, j, k))的代价为(a_i | (a_j & a_k)~(i < j < k))
现在你需要求出代价最大的三元组的代价是多少
(3 leq n leq 10^6, 0 leq a_i leq 2 imes 10^6)
Solution
我们可以考虑对于每一个(i)都找到最优的(a_j & a_k)来更新答案,所以我们需要用一个数据结构来维护(a_j & a_k)的最优值。接下来讲的东西可能会有一点抽象,所以没看懂的可以自行YY一下 然后去看代码
我们考虑对于把一个数字(x)插入到一个(trie)里面,那么对于所有的数字(y & x = y)都会产生(1)的贡献,我们在查询的时候只需要看查询位置的计数器是否达到(2)就可以了。因为达到(2)就表示有两个之前被插入的权值,这样我们就可以在这个里面快速查询能否存在(a_j & a_k)等于某个特定的权值的"超集"
这个东西有一个很神奇的性质,如果一个权值的计数器达到了(2),那么这个权值的所有"子集"位置的计数器肯定已经满了
然后查询的时候,我们就可以直接从高到底枚举每一个位置,然后贪心地去考虑就可以了,如果当查询值的这一位为(1)就直接跳过。否则就看一下前位的并能不能是(1),能就强行让它是(1),否则就跳过
G. Polygons
Description
给你一个圆形,你要在圆周上选择尽量小的的点数,使得你可以把(k)个边数互不相同的正多边形放入圆中并以你选择的点作为顶点,你选择的多边形的边数必须在(3)到(n)之间
(3 leq n leq 10^6, 1 leq k leq n - 2)
Solution
我们可以考虑把顶点看成分数去考虑,这样这道题就变成了每一个边数(x)对应一个分数集合({frac{0}{x}, frac{1}{x}, frac{2}{x}, cdots, frac{x - 1}{x}}),你需要选择(k)个不同的集合,使得他们的并大小尽量小
我们考虑选择一个(x)边形,增加的点数就是之前(x)未出现的因子的(varphi)之和
所以我们只需要特判一下(k = 1)的情况,其余情况就直接把(varphi)的前(n)项从小到大排序,取前(k + 2)项就可以了。因为对于一个(x)边形,它的因子对于(forall y, varphi(y) < varphi(x)),所以在选(x)之前肯定已经被选择了,所以我们就只需要考虑当前的(varphi)值就可以了
Educational Codeforces Round 71
D. Number Of Permutations
Description
给定(n)个二元组((a_i, b_i)),定义一个二元组排列不好,当且仅当下面两个条件至少有一个满足
- 二元组第一关键字为单调不降序列
- 二元组第二关键字为单调不降序列
求有多少个好的二元组,答案对(998244353)取模
(a_i, b_i leq n leq 3 imes 10^5)
Solution
我们可以考虑用容斥去计算答案,我们用总方案数(n!)减去满足条件(1)的,减去满足条件(2)的,再把重复减掉的满足两个条件的加回来
只需要满足一个条件很好搞,就直接排序然后求一下相同权值的区间长度阶乘的乘积就行了
如果需要同时满足两个条件,我们可以按照第一关键字排序,在内部再按照第二关键字排序。这时我们要判断一下全体第二关键字是不是非降的,否则就直接忽略掉这种情况。如果有这种情况,我们就把第二关键字连续段长度的阶乘乘起来就是同时满足两个条件的方案数了
E. XOR Guessing
Description
这是一道交互题
有一个隐藏的数字(x),你要通过不超过两次询问推出(x)的值。每次询问你可以给定一个长度恰好为(100)的序列,然后交互库会返回序列中随机一个位置的值异或上(x)的结果,任意一个数字只能在所有询问中出现一次
题目中所涉及到的所有权值均(in [0, 2^{14}))
Solution
这是一道降智的题目
这道题就直接用一次询问搞出(x)前(7)位的值,再用一次搞出后(7)位的值就没了,就是让询问的那(100)个数在某(7)位上都为(0)
F. Remainder Problem
Description
给你一个数列,需要支持(q)次下面两种操作
- 给位置(x)上的数字增加(y)
- 查询所有在模(x)意义下为(y)的位置上的值之和
(q leq 5 imes 10^5, TL = 4s)
Solution
我们不要被这道题的数据范围吓倒了,实际上这道题就直接分块就可以了
我们考虑对于(leq sqrt n)的模数预处理出每一个余数的答案,在修改的时候暴力更新一下就可以了
对于(> sqrt n)的模数就直接暴力遍历一遍查询即可
G. Indie Album
Description
给定一个大小为(n)的字符串集合({s_i}),对于其中某一个串(s_i)要么是(1)个字母,要么是在之前的某个串(s_j (j < i))的基础上加上(1)个字母
现在有(m)个询问,每次给定(i, t),表示询问字符串(t)在(s_i)中出现了多少次
(n, m, sum t leq 4 imes 10^5)
Solution
我们可以直接对于所有的字符串建立AC自动机,对于一次查询,我们就相当于是要把(s_i)在AC自动机上的所有点染色,然后查询(t)的末尾节点的(fail)树子树中有多少个节点被染了色。这个东西和[NOI2011]阿狸的打字机那题的套路一样
然后我们就可以在上面(dfs),再访问到一个节点的时候染上色,回溯的时候再撤回,并且用一个树状数组维护一下(fail)树的子树和。这样我们就可以保证遍历到所有的(s_i),然后我们就可以处理所有与(s_i)相关的询问了
另外这题还有一个常数小点的做法,可以只对所有的(t)串建AC自动机,实现过程和上面的做法差不多。区别就在于建立AC自动机的时候没有考虑(s_i)串,所以我们在(dfs)的时候,如果发现不能走了就直接(fail)。同时我们还要注意递归的深度,可以先把(s_i)串集合的(dfs)树先建出来,然后在(dfs)树和AC自动机上同时走
Educational Codeforces Round 72
A. Creating a Character
Description
给定三个整数(str, int, exp),你需要给(str)加上(a in N),给(int)加上(exp - a),使得(str > int)。问有多少个不同(a)能满足条件,数据组数(T)
(T leq 100, 1 leq str, int leq 10^8, 0 leq exp leq 10^8)
Solution
这题有下面几个限制:
- (int)加完之后不能超过加完之后的(a)
- (int)最多加(b + c) (可能(int)加不到(str))
- 方案数至少是(0)
然后就没了,可能有比较好的写法
B. Zmei Gorynich
Description
(zjm)有(x)个头,你有(n)种操作,每个操作用((a_i, b_i))表示,表示你可以最多砍掉(zjm)的(a_i)个头,然后(zjm)就会长出(b_i)个头。如果有任意时刻(zjm)没有头了,那你就取得了胜利。现在你可以按任意顺序执行这些操作,并且一种操作可以执行多次,问最少需要多少次操作,如果你永远砍不完(zjm)的头,那么输出(-1)
(x leq 10^9, n leq 100, 1 leq a_i, b_i leq 10^9)
Solution
我们就直接把(a_i - b_i)最大的和(a_i)最大的分别找出来,然后先用(a_i - b_i)最大的操作压低(zjm)头的个数,最后再用(a_i)最大的一刀砍下去结束游戏。如果没有(a_i > b_i)的操作,并且(x > max{a_i}),那么就无解
C. The Number Of Good Substrings
Description
给定一个长度为(n)的(01)串,问里面有多少个字串(连续的),将其看成一个右边为低位的二进制数后,其值等于这个串的长度
(n leq 2 imes 10^5)
Solution
我们只需要枚举所有长度不超过(lceil log n ceil)的字串,求出其表示的值,然后往前把字串扩展到其表示值的长度,检查一下前面那段是不是全为(0)就可以了
D. Coloring Edges
Description
给定一个(n)个点(m)条边的见简单无向图,你需要给每条边图一个颜色,需要满足不存在一个环上所有的边颜色相同,问最少需要多少种颜色,并输出染色方案
(2 leq n leq 5000, 1 leq m leq 5000)
Solution
这题是一道结论题,我们发现答案不会超过(2)。那么,我们就先判断一下这个图有没有环,如果没有答案就是(1)。否则答案为(2),我们就依次判断每一条有向边((a, b)),(a < b)的染一种颜色,否则染另外一种颜色,这样就能保证不会有环所有的边都是一种颜色了
E. Sum Queries?
Description
给定一个长度为(n)的序列(a_i),你需要支持(m)次下面两种操作:
- 把(x)位置的值修改为(y)
- 问区间([l, r])中,是否存在"不平衡"的子序列。定义一个序列不平衡,当且仅当这个序列的和在十进制下,存在一位上的数字,在序列的每一个数的该位上没有出现。如果存在就输出这个序列的和,否则输出(-1)
Solution
我们直接对于每一个数位都维护一下区间中这个数位上不为(0)的数字的最小和次小值就可以了,用线段树实现即可。但是在写的时候要注意一下常数,因为常数太大会被卡,在线段树上更新最小值和次小值的时候不要图省事直接(sort),其实只需要分两种情况讨论一下就可以了
F. Forced Online Queries Problem
Description
有(n)个初始互相独立的点,你需要支持(m)次以下两种操作:
- 在(x)和(y)之间连一条边,如果已经存在边,就不连并断开这条边
- 检查(x)和(y)的联通性,联通答案就是(1),否则答案为(0)
每次操作的(x)和(y)需要对于上次询问的答案(lans)(初始为(0))作如下运算,
(x = (x + lans - 1) mod n + 1, y = (y + lans - 1) mod n + 1)
(n, m leq 2 imes 10^5)
Solution
我们可以预处理出(1)操作可能出现的所有的边,以及每一条边的后面第一个再次出现这条边的位置。然后我们就直接线段树分治,按照这个问题的离线做法一样的方法,去维护一个可撤销并查集,把每条边出现的时间段上都打上这条边的标记
但是我们一开始并不清楚一条边对应的每一段到底是存在还是不存在,我们要到达一个时间段的左端点才知道。我们等到这个时候再修改就可以了,这就是一个动态维护这个线段树分治的过程
Summary
这场挂飞了,直接掉名了。考试的时候一直想睡觉,而且(A)题是一道细节题,这方面我一直不是很行。我搞了半个小时才过(A),这导致我后半场心态血崩——(B)题看错题,(C)题细节写挂
以后考试遇到这种细节题,一定要先把所有的细节写在纸上,把怎么实现想清楚在开始打代码