CF div2 1005 (A~E)
A
每次将当前 \(s\) 中以最左侧的连续一段 \(1\) 开头的后缀移动到 \(t\),在 \(t\) 中留下这段 \(1\),将剩下的后缀再移回 \(s\),循环模拟即可。
B
容易发现分数一定不会增加,只能尽可能保持不变。而题中还要在在分数最大情况下最小化数组长度,可以发现:只能删掉出现次数为 \(1\) 的数,否则若删去出现次数 \(>1\) 的数,分数必然减小,一定不如不删更优。而只能删其中一个子数组,则找出现次数为1的最长连续段即可。
C
一眼题。发现选择的任意正数一定在任意负数左侧,则相当于挑选一个左侧全是正数,右侧全是负数的最优子序列。
前缀和分别处理正数和负数,枚举分界位置即可。
D
字典树
题目相当于给点 \(x\),求一个最大的 \(i \in [1,n-1]\),使得 \(a[i] > sufxor[i+1] \oplus x\)
首先,通过二进制形式来判断 \(a > b\) 的方式:存在第 \(i\) 位:
- \(a_{i}=1,b_{i}=0\)
- 对于任意 \(j>i\),\(a\) 与 \(b\) 的第 \(j\) 位均相同。
可以发现这个信息与 相同前缀 是密切相关的,因此可以考虑字典树来维护。这里字典树维护的信息描述起来可能不是很好懂:
- 字典树上的某个结点 \(u\) 对应一个前缀 \(pre\),同时具有一个权值 \(w[u]\),表示 \(x\) 的前缀为 \(pre\) 时,原序列中的哪个位置开始出现 \(a[i] > sufxor[i+1] \oplus x\),也就是从 \(pre\) 的末位(假设是第 \(j\) 位)开始出现 \(a_{j}=1 且 sufxor[i+1]_{j} \oplus x_{j} = 0\),并且 \(w[u]=i\)。
令 \(a=a[i],b=sufxor[i+1]\),考虑找到所有满足上述条件的 \(a_{j},b_{j}\)。对于某一对 \((a[i],sufxor[i+1])\),同时从高到低看它们的每个二进制位。在第 \(i\) 位往前的 前缀 相同的情况下(在字典树内对应向下走若干步,走到了某个结点):
- 若 \(a_{i}\) 与 \(b_{i}\) (第 \(i\) 个二进制位上的数字)相同,则需要继续看后面的二进制位 -> 在字典树中等价于开一个新点并将指针移向这个新点。
- 若不同,则当 \(a_{i}=1且b_{i}=0\) 时,找到了一个符合要求的位置,需要做记录 -> 在字典树中体现为:在对应前缀的结点上记录该位置(若原来有位置,则取最大值,因为要找最先不合法的位置;并且以后再碰到有记录的点就也可以不用再继续看了);而当 \(a_{i}=0且b_{i}=1\) 时,就不需要看后面的二进制位了,因为一定能保证 \(a <= b\) -> 在字典树中等价于什么都不做。(目的是找到所有满足上述条件的位置并在相应前缀上做记录,发现后续不可能再满足上述条件就不需要再开点继续向低位看了)
将所有 \((a[i],sufxor[i+1])\) 对应全部 \(x\) 可能的情况插入字典树后,每个询问可 \(O(logV)\) 来处理:
对于任意询问 \(x\),直接跑一下字典树,在路径上经过的结点权值的最大值即为 原序列中最先满足 \(a[i] > sufxor[i+1] \oplus x\) 的位置 \(i\)。具体细节见代码。
还有一个比字典树更好且更易理解的做法,是b站up主yvbf的做法,听完讲解后大彻大悟,感觉是非常好的方法。这里直接贴代码和讲解视频链接,不写题解了。
E
正解应该是并查集维护,但是写了个 \(set\) 模拟也过了。。。感觉这题根本没有 \(2400\)。
大致想一下就可以发现这道题就相当于:按排列 \(p\) 的顺序删颜色块,每次可以删对应位置的相同颜色连续段中的任意一个块,总方案数。难点在于删的过程中可能会有两段相同颜色段合并不太好维护。但用 \(set\) 来模拟就特别 \(easy\)。具体细节见代码。
又补了一个双向链表 + 并查集的做法:
code