2023.12.4 近期练习
CF1845E
这种 \(01\) 串的描述方式一般是提出 \(1\) 的位置去讨论,设原串 \(1\) 出现位置是 \(p_1,...,p_m\).
考虑最后生成的串的性质,描述其 \(1\) 的位置,\(q_1,...q_m\)。
那么至少移动步数为 \(\sum |p_i-q_i|\),因为 \(1\) 的位置是相对不变的。
考虑一个一个 \(1\) 往里填,设 \(f_{i,j,k}\) 表示前 \(i\) 个位置,填入了 \(j\) 个数,\(\sum_{t=1}^j |p_t-q_t|=k\)。
然而这样是 \(n^3\) 的,我们考虑优化状态。
设 \(sum_i\) 表示前 \(i\) 个数有多少个 \(1\)。
如果当前填到第 \(i\) 个位置,设 \(d=j-sum_i\),那么就意味着有 \(d\) 个 \(1\) 要挪进来或移出。
然而这样至少移动是 \(d^2\) 的,所以 \(|d|\le \sqrt k\)。
那么将 \(f_{i,j,k}\) 定义改为填入了 \(sum_i+j\) 个数,且 \(|j|\le \sqrt k\)。
复杂度是 \(nk^{1.5}\)。
这种问题首先应该学会优化状态表达,其次是答案上限的注意。
CF1859E
这个题有一个肉眼可见的 dp,设 \(f_{i,j}\) 表示处理到前 \(i\) 个数组成了区间,当前区间长度是 \(j\) 的最大值。
然而转移是 \(O(n)\) 的,所以总共的复杂度是 \(O(n^3)\).
考虑寻找性质。发现可以利用绝对值的性质,由于 \(x\le |x|\),所以我们可以直接把绝对值拆开。
题目要求最大值。两个绝对值直接拆成四个方面,总会遇到最大值。
\(f_{i,j}\) 的转移可以从 \(i'-j'=i-j\) 的转移而来,对于 \(i-j\) 相同的,维护四个方面的最大值。
转移变成 \(O(1)\),总的复杂度是 \(O(n^2)\)。
这种问题应掌握绝对值的性质。
CF1903D2
一开始想到一个很烂的 dp,\(f_{i,j}\) 表示前 \(i\) 个数,当前二进制与起来是 \(j\),至少花了 \(k\)。
然而这样是 \(nV\) 的复杂度。\(V\) 是答案的大小。而如果存在有 \(V\) 在复杂度里就是不能接受的。
如果把 \(j\) 这维状态去掉呢?由于题目有关二进制,猜到要拆位能否?
从答案的高位开始贪心,每位判断是否能填 \(1\) 进去,能填即填。如何判断呢?
设当前判断到第 \(j\) 位,计算每一个 \(a_i\) 的第 \(j\) 位变成 \(1\) 至少的代价,只需要判断当前 \(k\) 是否还够用即可。
如果只有 \(1\) 次询问,不如直接使用 \(n\log V\) 的复杂度。但是有 \(1e6\) 次询问,如何加速询问呢?
考虑操作本身,考虑第 \(j\) 位,每次操作就是令没被操作过的第 \(j\) 位设为 \(1\)(已经为 \(1\) 的不设)。
和加上被操作过的(被操作过的末尾一定全部是 \(0\))。
注意到 \(a_i\le 1e6\)。所以如果有不设的,\(j\le 20\)。
我们可以预处理出 \(j\le 20\) 的所有情况下,代价的多少。
预处理的话用高维前缀和,在这里写了很久,应该转化为超集和计算,就卡在这里。
这种问题应该考虑拆位,以及预处理,SOS dp。
CF1858E1/E2
先想到处理回滚的操作。
所以我们需要维护之前的版本,如果把操作看成树形结构,那么回滚就是跳父亲节点。
如果每次回滚都暴力的处理,那么如果一直回滚 \(-k\) 操作,则一定超时。
考虑优化,如果树上只存 \(+\),发现 \(-k\) 操作其实就是跳 \(k\) 级祖先。
查询数列中不同的数的数量,我们考虑维护一个值域线段树,只需要记录每个数出现多少次即可。
同时,维护一个变量记录当前有多少个不同的数。
维护每个版本的信息我们想到使用主席树,只需要在操作树每个节点维护主席树。
想到有一个问题,节点的数量达到了 \(2e7\)。导致空间开不下。
事实上,由于优化到只有加数,我们只需要维护的是每个点是否有值,就是 01,
那么可以用 bitset 维护,节省空间。
想到最后,我们也无需建树,只需一个栈即可。
最后观察题解,发现可以通过记录第一次出现的位置来维护。
这种问题考虑操作树,另外是 bitset 优化空间。
CF1854C
毫无思路。
由于题目给是的集合非常难处理,考虑允许多重集的情况。
如果是多重集,那么答案很显然是 \(\sum m+1-a_i\)。
考虑两种情况的差值,发现如果 \(a_i=a_j\) 了,那么答案就会减少 \(m+1-a_i\)。
而 \(a_i=a_j\) 只会出现 \(a_i=a_{i+1}\),如果钦定 \(a_{i+1}=a_j\) 时不会消失,那么移动 \(i\) 和 \(i+1\) 是等概率的。
用 dp 解决这个问题,设 \(f_{i,j}\) 表示 \(A\) 走了 \(i\) 步,\(B\) 走了 \(j\) 步的概率。
最后,对于每个相邻的数对,若 \(a_{i+1}\) 与 \(a_i\) 差值为 \(i-j\),那么令 \(Ans\) 减去 \(f_{i,j}\times (m+1-(a_i+j))\)。
这种问题应该从反面思考,并拆贡献。
CF1842F
观察样例得到:选的 \(k\) 个点一定要相邻,否则中间的边就不优。
尝试增量式维护?考虑从 \(k\) 个点全选开始,每次删掉一个叶子节点?
思考了很久发现这样难以做,因为这样是 \(O(n^3)\)。
瓶颈在于绝对值的处理?
考虑重心的性质,钦定 \(k\) 个点的重心为 \(r\),因为有重心的性质,每个子树黑点个数不过 \(n/2\)。
所以直接去绝对值,答案为 \(\sum _{i\neq r} k-2siz_i\)。
而我们每新加一个点,都给每个非根的祖先带来 \(-2\) 的贡献,所以每次选深度最小的即可。
所以我们不妨枚举所有 \(k\) 点的重心,然后求解。
如果重心枚举错了也没事,因为 \(x\le |x|\),答案只会更小,但始终能枚举到正确答案。
这种问题考虑绝对值与重心的关系。
CF1838E
一眼,猜想 \(A\) 数组是没有用的。
不妨把 \(A\) 都设成 \(1\),问题转化为,问多少个 \(B\) 可以至少存在 \(n\) 个 \(1\)。
考虑容斥,减去哪些 \(B\) 中的 \(1\) 个数少于 \(n\) 的。
也就是求 \(\sum_{i=0}^{n-1} C(m,i)\times (k-1)^{m-i}\)。
这个直接暴力即可,组合数可以递推。
这种问题应该多观察样例,找规律。
CF1840G1/G2
如果我们旋转 \(k\),且转出来的数与一开始的数相同,那么 \(n|k\)。
感觉这样做,如果遇到 \(n\) 是大质数就很难判断出来。
上面方法无法优化,我们如果设转一次是一种置换 \(p\),我们现在求 $p^{n}=\epsilon $。
求高次方程想到 BSGS。
考虑先转 \(1000\) 次,每次转 \(1\),如果有重复的,那么就得到了 \(n\)。
否则,每次转 \(1000\),直到有重复的,那么就得到了答案。
这样最多转 \(2000\) 次,能通过 easy 版。
但是我们没有充分地利用其回答的性质,当询问回答 \(x\),就意味着 \(x\le n\)。
我们转随机 \(k\) 个,设最大值为 \(n_0\),钦定 \(n_0\le n\le n_0+d\),然后直接在 \(d\) 内上 BSGS。
具体是,先直接转 \(n_0\),因为 \(0\le n-n_0\le d\),所以只用做 \(2\sqrt d\) 次。
设 \(k=400,d=90000\),即可。
然而有问题,\(n\ge n_0+d\),但是这样的概率很小,为 \((\frac{n-d-1}{n})^k\)。
这种问题应考虑随机的性质,和根号算法。